Skip to content

Instantly share code, notes, and snippets.

@texdraft
Last active August 12, 2019 23:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save texdraft/fb93aa8edca268f657895ff8c4b529b0 to your computer and use it in GitHub Desktop.
Save texdraft/fb93aa8edca268f657895ff8c4b529b0 to your computer and use it in GitHub Desktop.
Sketching goto in TeX (with a bonus at the end)
% Towards goto in plain TeX.
% This works in LaTeX (after adding \documentclass and
% \begin{documen} too.
% Surely this would have more legitimacy if it were wrapped in a snazzy macro
% that let you say \goto and not manually set the program counter.
% E.g.:
%
% \tagbody{ % (homage to Common Lisp)
% \label\A
% first
% \goto\C
% \label\B
% third
% \return
% \label\C
% second
% \goto\B
% }
%
% If implementing this for LaTeX, \label should be changed to something else,
% such as \lab. As another Lisp homage, I'd suggest \tag, but that's a LaTeX
% command too. Or redefine \label (or \tag) within \tagbody. Of course, in
% LaTeX \tagbody should probably be made implemented as an environment.
% I believe that there's a BASIC interpreter embedded in Haskell as a DSL which
% works by ``overloading'' numbers to handle the beginning line number labels.
% A similar solution could be pursued so something like
%
% \tagbody {
% 1:
% first
% \goto3
% 2:
% third
% \return
% 3:
% second
% \goto2
% }
%
% would work, using some magic (looking ahead to see if sequence of digits ends
% with a colon, perhaps). A different approach could be taken to allow the
% following to be possible:
%
% \tagbody {
% \A:
% first
% \goto\C
% \B:
% third
% \return
% \C:
% second
% \goto\B
% }
%
% (Possible solution: make some other character the escape character, like the
% at sign, make backslash active, and do @def\#1:{...} to be like \label#1, if
% the control sequence named by #1 isn't already defined (to allow people to
% use commands followed by colons in ordinary test); if it is defined then just
% expand into that command.)
% \goto could possibly be defined to immediately end execution and
% ``jump'' to the label reqeusted.
% Anyway, here are some ways to approach goto-like functionality.
% See also Guy Steele's ``Debunking the `expensive procedure call' myth''
% (AKA Lambda: The Ultimate GOTO) at https://dspace.mit.edu/handle/1721.1/5753.
\newcount\pc
\pc=0
\newif\ifnotdone
\let\return=\notdonefalse
% Simple version
% The output is A B C D E F G
\notdonetrue
\loop
\ifnum\pc=0 %
\immediate\write16{Entry}
\pc=1
\else\ifnum\pc=1 %
A
\message{Statement 1}
\pc=3
\else\ifnum\pc=2 %
G
\message{Statement 2}
\return
\else\ifnum\pc=3 %
B
\message{Statement 3}
\pc=6
\else\ifnum\pc=4 %
D
\message{Statement 4}
\pc=7
\else\ifnum\pc=7 %
E
\message{Statement 7}
\pc=5
\else\ifnum\pc=6 %
C
\message{Statement 6}
\pc=4
\else\ifnum\pc=5 %
F
\message{Statement 5}
\pc=2
\fi\fi\fi\fi\fi\fi\fi\fi
\ifnotdone
\repeat
% Here's a different form, which allows for code to happen between
% ``branches''.
% Each backwards jump causes another iteration, whereas in the previous one
% every single jump caused another iteration; therefore, this version might be
% faster. In this case, there are two, so LOOP is printed three times.
\pc=0
\notdonetrue
\loop\immediate\write16{LOOP}% this prints every iteration
\ifnum\pc=0%
\immediate\write16{Entry}
\pc=1
\fi
\ifnum\pc=1 %
A
\message{Statement 1}
\pc=3
\fi
\message{This will also print every iteration,.}
\ifnum\pc=2 %
G
\message{Statement 2}
\return
\fi
\ifnum\pc=3 %
B
\message{Statement 3}
\pc=6
\fi
\ifnum\pc=4 %
D
\message{Statement 4}
\pc=7
\fi
\ifnum\pc=7 %
E
\message{Statement 7}
\pc=5
\fi
\ifnum\pc=6 %
C
\message{Statement 6}
\pc=4
\fi
\ifnum\pc=5 %
F
\message{Statement 5}
\pc=2
\fi
\ifnotdone
\repeat
% Here's one with only backwards branches (very inneficient), and with font
% changing. Each font command only affects the branches above it, and until the
% next font command. More statements have been added to better illustrate this.
\pc=0
\notdonetrue
\loop\immediate\write16{LOOP}% prints 11 times
\ifnum\pc=1 % Roman J
J
\message{Statement 1}
\return
\fi
\ifnum\pc=2 % bold I
I\rm
\message{Statement 2}
\pc=1
\fi
\ifnum\pc=3 % Roman H
H\bf
\message{Statement 3}
\pc=2
\fi
\ifnum\pc=4 % italic G
G\rm
\message{Statement 4}
\pc=3
\fi
\ifnum\pc=5 %
F% italic F
\message{Statement 5}
\pc=4
\fi
\ifnum\pc=6 %
E% italic E
\message{Statement 6}
\pc=5
\fi
\ifnum\pc=7 % slanted D
D\it
\message{Statement 7}
\pc=6
\fi
\ifnum\pc=8 % bold C
C\sl
\message{Statement 8}
\pc=7
\fi
\ifnum\pc=9 %
B% bold B
\message{Statement 9}
\pc=8
\fi
\ifnum\pc=10 % Roman A
A\bf
\message{Statement 10}
\pc=9
\fi
\ifnum\pc=0 %
\immediate\write16{Entry}
\pc=10
\fi
\ifnotdone
\repeat
% Finally, here's something like Duff's device, whose original form is the
% following (in pre-ANSI C):
% send(to, from, count)
% register short *to, *from;
% register count;
% {
% register n=(count+7)/8;
% switch(count%8) {
% case 0: do{ *to = *from++;
% case 7: *to = *from++;
% case 6: *to = *from++;
% case 5: *to = *from++;
% case 4: *to = *from++;
% case 3: *to = *from++;
% case 2: *to = *from++;
% case 1: *to = *from++;
% } while(--n>0);
% }
% }
% (See https://www.lysator.liu.se/c/duffs-device.html)
% This is just a curiosity and not really agreat showcase of a goto. It's also
% untested, and wouldn't actually `work' regardless: \duffsend will certainly
% NOT ``copy an array of shorts to the Programmed IO data register of an Evans
% & Sutherland Picture System II'' as the original does :).
% There's surely a way to simulate C's pointers etc. to make it functional, but
% (1) why? and (2) that's difficult.
% Parameters
\newcount\ptrto % register short *to
\newcount\ptrfrom % register short *from
\newcount\regcount % register [int] count
% Note that TeX has special meanings of both `register' and `count'!
% You could use e-TeX's \numexpr to simplify this, but this works with any
% engine.
\newcount\n % = (count+7)/8
\n=\regcount
\advance\n by 7
\divide\n by 8
\newcount\swcount % will be (count%8)
\swcount=\regcount
\divide\swcount by 8
\multiply\swcount by 8
\multiply\swcount by -1
\advance\swcount by \regcount\relax
\pc=0
\notdonetrue
\def\duffsend{
\loop
\pc=\swcount % switch(count%8) {
\ifnum\pc=0 % case 0: do {
\advance\ptrfrom by 1% *from++
\advance\ptrto by \ptrfrom % *to = *from
\pc=7
\fi
\ifnum\pc>6 % case 7:
\advance\ptrfrom by 1
\advance\ptrto by \ptrfrom
\pc=7
\fi
\ifnum\pc>5 % case 6:
\advance\ptrfrom by 1
\advance\ptrto by \ptrfrom
\pc=7
\fi
\ifnum\pc>4 % case 5
\advance\ptrfrom by 1
\advance\ptrto by \ptrfrom
\pc=7
\fi
\ifnum\pc>3 % case 4
\advance\ptrfrom by 1
\advance\ptrto by \ptrfrom
\pc=7
\fi
\ifnum\pc>2 % case 3
\advance\ptrfrom by 1
\advance\ptrto by \ptrfrom
\pc=7
\fi
\ifnum\pc>1 % case 2
\advance\ptrfrom by 1
\advance\ptrto by \ptrfrom
\pc=7
\fi
\ifnum\pc>0 % case 1
\advance\ptrfrom by 1
\advance\ptrto by \ptrfrom
\pc=7
\fi
\advance\n by -1 % --n
\ifnum\n=0 % } while(n>0)
\return
\fi
\ifnotdone
\repeat % }
}
\end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment