summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/frankenstein/allocate.sto
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/frankenstein/allocate.sto
Initial commit
Diffstat (limited to 'macros/latex/contrib/frankenstein/allocate.sto')
-rw-r--r--macros/latex/contrib/frankenstein/allocate.sto442
1 files changed, 442 insertions, 0 deletions
diff --git a/macros/latex/contrib/frankenstein/allocate.sto b/macros/latex/contrib/frankenstein/allocate.sto
new file mode 100644
index 0000000000..d907cecec3
--- /dev/null
+++ b/macros/latex/contrib/frankenstein/allocate.sto
@@ -0,0 +1,442 @@
+% \AddToCheckSum{309}
+%
+% \begin{warning}
+% This code has not been well tested yet. The output routine of \LaTeX{} is
+% very complicated, and unforseen problems might arise.
+% \end{warning}
+%
+% \DescribeMacro\NextAux
+% \DescribeMacro\DynamicAux
+% \DescribeMacro\StaticAux
+% The macro \cs\NextAux changes \cname{@auxout} to a new stream if one is
+% available, and gives an error otherwise. The macro is implemented in
+% dynamic and static ways which can be selected with \cs\DynamicAux and
+% \cs\StaticAux\marg{number of streams}. The number of streams can be from 2
+% to~16. The dynamic implementation is the default; I do not see much use
+% for the static implementation at present. The \option{static} option is
+% the equivalent of the declaration |\StaticAux{10}|. The \option{dynamic}
+% selects the dynamic implementation.
+%
+% \cs\StaticAux can be invoked after \cs\DynamicAux, but not the other way
+% around (at least, the streams allocated by \cs\StaticAux are not
+% recovered). Macros which use \cs\NextAux do not have to know whether the
+% implementation is static or dynamic.
+%
+% \subsubsection{Wheels}
+%
+% The output streams are manipulated with the help of a data structure I call a
+% \word{wheel}.
+%
+% A \term{wheel} has 0 or more \term{spokes} and can be \term{rolled}. Each
+% spoke is a \TeX{} token, probably a control sequence name, and has an
+% internal name. You can access the spoke at the 12 o'clock or ``top''
+% position of a wheel. In computerese, a wheel is a circularly linked list of
+% tokens, and the operation of rolling moves a pointer along it in a certain
+% direction by one element.
+%
+% Wheels and operations on wheels are local.
+%
+% \DescribeMacro\InitWheel
+% \DescribeMacro\DefWheel
+% You make a wheel either with \cs\InitWheel\marg{\\csname}, which makes
+% \meta{\\csname} a wheel with no spokes, or
+% \cs\DefWheel\marg{\\csname}\marg{spokes}, which makes a wheel with
+% \meta{spokes} for spokes. The first spoke in \meta{spokes} is the top, the
+% second will be top after one roll, and the first will be top again after
+% $n$ rolls, if there are $n$ spokes.
+%
+% \DescribeMacro\Roll
+% \DescribeMacro\Top
+% \DescribeMacro\AddSpokes
+% Wheels are rolled by \cs\Roll\marg{wheel}. Spokes can be added to a
+% wheel with \cs\AddSpokes\marg{wheel}\marg{spokes}. When $n$ spokes are
+% added, the previous top will be at the top after $n$ rolls.
+% \cs\Top\marg{wheel} expands eventually to the top spoke, which then can
+% further expand, and so on.
+%
+% \DescribeMacro\IfTop
+% \cs\IfTop\marg{wheel}\marg{spoke}\marg{true clause}\marg{false clause}
+% compares the top of \meta{wheel} with \meta{spoke} using \cname{ifx}, and
+% executes either \meta{true clause} or \meta{false clause} as appropriate.
+% (The \package{newclude} package doesn't actually use this command; it's
+% provided to ``round out'' the wheel data structure.)
+%
+% \caveat{Don't put more than one token as the second argument to \cs\IfTop.}
+%
+% \subsubsection{Preliminaries}
+%
+% We require the \package{afterpage} package. The intuitive justification is
+% that \cs\write{}s are delayed until the current page is shipped out. We need
+% to keep an output stream open until its last \cs\write has been actually
+% handled; after that, the stream can be made available again.
+% \begin{macrocode}
+\RequirePackage{afterpage}
+% \end{macrocode}
+%
+% \begin{macro}{\nc@aux@wheel}
+% We use the wheel structure to handle both the static case and the dynamic
+% case. The spokes of the wheel are macros |\nc@auxout@|\meta{n}. Their
+% first-level expansion is \meta{n}, a positive integer from 0 to~15. Each
+% spoke has two corresponding macros. |\nc@auxout@|\meta{n}|@stream| is a
+% stream name allocated by \cs\newwrite. |\nc@auxout@|\meta{n}|@inuse| is a
+% global boolean which is |true| iff the corresponding stream is currently in
+% use.
+% \begin{macrocode}
+\InitWheel\nc@aux@wheel
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\nc@count}
+% We need an internal counter. Notice that the stream numbers used in the
+% auxwheel start at 0, so the stream associated with with the numeral 4 is
+% the fifth stream.
+% \begin{macrocode}
+\newcounter{nc@count}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\nc@aux@wheel@size}
+% \cname{nc@aux@wheel@size} is a pseudo-counter that holds the present size
+% of the aux wheel. In the static case it never changes and is set only for
+% consistency.
+% \begin{macrocode}
+\ReserveCS\nc@aux@wheel@size
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\NextAux}
+% \mbox{}
+% \begin{macrocode}
+\ReserveCS\NextAux
+% \end{macrocode}
+% \end{macro}
+%
+% The kernel allocates two streams for the include system, \cname{@mainaux} and
+% \cname{@partaux}. The auxwheel is initialized with these two streams. The
+% first, corresponding to the principle source, is always marked in use.
+%
+% \todo{Reserve the stream names.}
+% \begin{macrocode}
+\newboolean{@nc@auxout@1@inuse@}
+
+\ReserveName{nc@auxout@1}
+\NewName*{nc@auxout@1} {} {1}
+
+\ReserveName{nc@auxout@1@stream}
+\expandafter\let\csname nc@auxout@1@stream\endcsname\@partaux
+% \end{macrocode}
+%
+% \begin{macro}{\nc@init@aux@wheel}
+% We initialize the wheel with the first spoke.
+% \begin{macrocode}
+\newcommand\nc@init@aux@wheel {%
+ \EExpand\csname nc@auxout@1\endcsname\In {%
+ \AddSpokes\nc@aux@wheel##1%
+ }
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Static allocation}
+%
+% \begin{macro}{\StaticAux}
+% nonpositive numbers are treated the same as~1. \todo{bounds check; the
+% counter's max should be one less than the number, since we have stream 0.}
+% \begin{macrocode}
+\newcommand\StaticAux [1] {%
+ \def\nc@aux@wheel@size {#1}
+ \setcounter{nc@count}{2}
+ \nc@init@aux@wheel
+ \@whilenum \value{nc@count} <= \nc@aux@wheel@size
+ \do {%
+% \end{macrocode}
+% First define the macros that make up the wheel itself to be their spoke
+% numbers.
+% \begin{macrocode}
+ \eExpand*\thenc@count\In {%
+ \NewName*{nc@auxout@\thenc@count} {} {%
+ ##1%
+ }%
+ }
+% \end{macrocode}
+% Next allocate the corresponding stream.
+% \begin{macrocode}
+ \EExpand\csname nc@auxout@\thenc@count@stream\endcsname\In {%
+ \@nameuse{newwrite}##1%
+ }
+% \end{macrocode}
+% Next create the corresponding flag (they start |false|).
+% \begin{macrocode}
+ \provideboolean{@nc@auxout@\thenc@count @inuse@}
+% \end{macrocode}
+% Now add the spoke itself.
+% \begin{macrocode}
+ \EExpand\csname nc@auxout@\thenc@count\endcsname\In {%
+ \ReserveCS#1%
+ \AddSpokes\nc@aux@wheel##1%
+ }
+ \stepcounter{nc@count}
+ }
+ \def\NextAux {%
+ \Roll\nc@aux@wheel
+ \@nameuse{if@nc@auxout@\Top\nc@aux@wheel @inuse@}%
+ \MonsterError{newclude} {%
+ You can't nest \protect\include this deeply!%
+ }%
+ \else
+ \Elet\@auxout\csname nc@auxout@\Top\nc@aux@wheel @stream\endcsname
+ \fi
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Dynamic allocation}
+%
+% \begin{macro}{\DynamicAux}
+% \begin{macro}{\nc@addnewauxstream}
+% \mbox{}
+% \begin{macrocode}
+\newcommand\DynamicAux {%
+ \def\nc@aux@wheel@size {1}
+ \nc@init@aux@wheel
+ \def\NextAux {%
+ \Roll\nc@aux@wheel
+ \@nameuse{if@nc@auxout@\Top\nc@aux@wheel @inuse@}%
+ \nc@addnewauxstream
+ \fi
+% \end{macrocode}
+% Either the top spoke was not in use, or we have added a fresh spoke at the
+% top -- so the top spoke represents what we want.
+% \begin{macrocode}
+ \Elet\@auxout\csname nc@auxout@\Top\nc@aux@wheel @stream\endcsname
+ \typeout{NextAux has just set auxout to stream
+ \the\csname nc@auxout@\Top\nc@aux@wheel @stream\endcsname.
+ We are using spoke number
+ \csname nc@auxout@\Top\nc@aux@wheel\endcsname.}
+ }%
+}
+% \end{macrocode}
+% It works out that the new spoke should have a spoke number one greater than
+% the current wheel size. We use the |nc@count| counter to find this number.
+% \begin{macrocode}
+\newcommand\nc@addnewauxstream {%
+ \setcounter{nc@count}{\nc@aux@wheel@size}%
+ \stepcounter{nc@count}%
+ \typeout{Allocating another spoke (spoke number \thenc@count)}%
+% \end{macrocode}
+% First we add the spoke itself, then initialize the corresponding objects.
+% \begin{macrocode}
+ \EExpand*\csname nc@auxout@\thenc@count\endcsname\In {%
+ \AddSpokes\nc@aux@wheel##1%
+ }%
+ \EExpand*\thenc@count\In {%
+ \DefName*{nc@auxout@##1} {} {##1}%
+ \provideboolean{@nc@auxout@##1@inuse@}%
+ \def\nc@aux@wheel@size {##1}%
+ \EExpand*\csname nc@auxout@##1@stream\endcsname\In {%
+ \@nameuse{newwrite}####1%
+ }%
+ }%
+}
+\DynamicAux
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Including}
+%
+% \begin{macro}{\nc@include}
+% The only way I see how to set the |inuse| flag to |false| at the proper
+% time is to use the \package{afterpage} package. What I would really like
+% is to \cs\write something with side effects.
+% \begin{macrocode}
+\newcommand\nc@include [2][] {%
+ \if@filesw
+ \immediate\write\@mainaux{\string\@input{#2.aux}}%
+ \fi
+ \@tempswatrue
+ \if@partsw
+ \@tempswafalse
+ \edef\reserved@b{#2}%
+ \@for\reserved@a:=\@partlist\do
+ {\ifx\reserved@a\reserved@b\@tempswatrue\fi}%
+ \fi
+ \if@tempswa
+ \stepcounter{IncludeDepth}%
+% \end{macrocode}
+% \cname{nc@t@c} is going to preserve the current \ext{aux} stream number to
+% restore \cname{@auxout}, in case there is a nested \cs\include.
+% \begin{macrocode}
+ \edef\nc@t@c {%
+ \the\@auxout
+ }%
+ \if@filesw
+ \NextAux
+ \openout\@auxout #2.aux
+ \write\@auxout{\relax}%
+ \expandafter\global
+ \csname @nc@auxout@\Top\nc@aux@wheel @inuse@true\endcsname
+% \end{macrocode}
+% \begin{macro}{\nc@include@finish@<N>}
+% The next line defines the macro |\nc@include@finish@|\meta{n} to close the
+% output stream that is presently open. We have an interesting task here of
+% getting certain unique information to macros after the \cname{@input} when we
+% might end up recursing during the \cname{@input}. To do this, we immediately
+% expand the variables we need and store them in a macro which will \emph{not}
+% be altered by a recursion of \cs\include. We have set up the |IncludeDepth|
+% counter to allow us to define such a macro, which is unique to \emph{this}
+% instance of \cs\include.
+%
+% \begin{warning}
+% The macro names |\nc@include@finish@|\meta{n} where \meta{n} is an
+% integer are overwritten, that is, they are not allocated in a safe way.
+% \end{warning}
+%
+% The following lines are intended to make this definition, where |<D>|
+% represents the current value of |IncludeDepth|, |<P>| represents the spoke
+% number of the current top of \cname{nc@aux@wheel}, and |<S>| represents the
+% stream number for the current part, i.e., the current value of
+% \cname{@auxout}, and |<X>| represents the stream number that was current
+% before \cs\include got called (this is saved in \cname{nc@t@c}).
+% \begin{codeexample}
+% \def\nc@include@finish@<D> {%
+% \closeout<S>%
+% \global\chardef\@auxout=<X>%
+% \afterpage{\global\@nc@auxout@<P>@inuse@false}
+% }
+% \end{codeexample}
+% \begin{macrocode}
+ \EExpand\theIncludeDepth\In {% ##1
+ \EExpand\the\@auxout\In {% ####1
+ \DefName{nc@include@finish@##1} {} {%
+ \closeout####1%
+ \global\chardef\@auxout=\nc@t@c
+ \typeout{Restored auxout to stream number
+ \nc@t@c \space (old: \the\@auxout)}
+ \typeout{executing afterpage}%
+ \EExpand\csname @nc@auxout@\Top\nc@aux@wheel
+ @inuse@false\endcsname\In {% ########1
+ \afterpage{%
+ \typeout{Finishing. auxout is now \the\@auxout; current spoke
+ is \csname nc@auxout@\Top\nc@aux@wheel\endcsname\space
+ with stream number
+ \the\csname nc@auxout@\Top\nc@aux@wheel @stream\endcsname
+ }%
+ \global########1%
+ }% Afterpage
+ }% EExpand
+ }% DefName
+ }}% EExpand
+ \fi % \if@filesw
+ \nc@t@b % surround the \include with something
+% \end{macrocode}
+% Now execute the text of the optional argument to \cs\include. Notice that if
+% we change to a new \ext{aux} file, we should do it before the optional
+% argument. This is important so that sectioning commands will appear in the
+% right order. If the sectioning command were to write to \cname{@mainaux},
+% then it would come \emph{after} the whole included \ext{aux} file, instead of
+% before it.
+% \begin{macrocode}
+ #1%
+ \@input@{#2.tex}%
+ \@writeckpt{#2}%
+ \if@filesw
+ \csname nc@include@finish@\theIncludeDepth\endcsname
+ \fi
+ \nc@t@b % surround the \include with something
+% \end{macrocode}
+% We mustn't restore the counter before we have finished using it.
+% \begin{macrocode}
+ \addtocounter{IncludeDepth}{\m@ne}%
+% \end{macrocode}
+% If the file is excluded by the \cs\includeonly command, we don't load it and
+% execute the file's checkpoint instead.
+% \begin{macrocode}
+ \else
+ \@nameuse{cp@#2}%
+ \fi
+}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Checkpoints}
+%
+% \begin{macro}{\@writeckpt}
+% \begin{macro}{\@wckptelt}
+% We must redefine the macros which write the checkpoints. \cname{@auxout}
+% is substituted for \cname{@partaux}; I think this change should be in the
+% kernel anyway! And we remove the \cs\immediate{}s.
+% \begin{macrocode}
+\defcommand\@writeckpt [1] {%
+ \if@filesw
+ \write\@auxout{\string\@setckpt{#1}\@charlb}%
+ \begingroup
+ \let\@elt\@wckptelt
+ \cl@@ckpt
+ \endgroup
+ \write\@auxout{\@charrb}%
+ \fi
+}
+\defcommand\@wckptelt [1] {%
+ \protected@write\@auxout{}{\string\setcounter{#1}{\the\@nameuse{c@#1}}}%
+}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Wheels}
+%
+% \begin{macro}{\InitWheel}
+% \begin{macro}{\Roll}
+% \begin{macro}{\IfTop}
+% \begin{macro}{\Top}
+% \begin{macro}{\AddSpokes}
+% A wheel is implemented as a macro. The tokens of its first-level expansion
+% are the spokes, the top being the first.
+% \begin{macrocode}
+\newcommand\InitWheel [1] {% args: wheel
+ \InitCS#1%
+}
+\newcommand\Roll [1] {% args: wheel
+ \edef #1{%
+ \expandafter\nc@roll #1\nc@llor
+ }%
+}
+\ReserveCS\nc@llor
+\NewNameDef{nc@roll} {#1\nc@llor} {%
+ \@cdr#1\@nil\@car#1\@nil
+}
+\newcommand\Top [1] {% args: wheel
+ \E@car #1\@nil
+}
+\newcommand\IfTop [4] {% args: wheel token true false
+ \EExpand#1\In {%
+ \edef\nc@t@b {%
+ \expandafter\noexpand\@car##1\@nil
+ }%
+ }%
+% \end{macrocode}
+% At this point, the first-level expansion of \cname{nc@t@b} is a single token,
+% the top of the wheel. We \cs\let \cname{nc@t@a} to this token.
+% \begin{macrocode}
+ \Elet\nc@t@a\nc@t@b
+ \let\nc@t@b #2%
+ \ifx\nc@t@a\nc@t@b
+ \expandafter\@firstoftwo
+ \else
+ \expandafter\@secondoftwo
+ \fi
+}
+\newcommand\AddSpokes [2] {% args: wheel spokes
+ \EExpand#1\In {%
+ \def #1{#2##1}%
+ }%
+}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}