TeX, 216 bytes (4 lines, 54 characters each)
Because it's not about the byte count, it's about the quality of the typeset output :-)
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
Try it Online! (Overleaf; not sure how it works)
Full test file:
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
\S{swap the a first and last letters of each word}
pwas eht a tirsf dna tasl setterl fo hace dorw
\S{SWAP THE A FIRST AND LAST LETTERS OF EACH WORD}
\bye
Output:

For LaTeX you just need the boilerplate:
\documentclass{article}
\begin{document}
{\let~\catcode~`A13 \defA#1{~`#113\gdef}AGG#1{~`#1 13%
\global\let}GFF\elseGHH\fiAQQ{Q}AII{\ifxQ}AEE#1#2#3|{%
I#3#2#1FE{#1#2}#3|H}ADD#1#2|{I#1FE{}#1#2|H}ACC#1#2|{D%
#2Q|#1 }ABBH#1 {HI#1FC#1|BH}\gdef\S#1{\iftrueBH#1 Q }}
\S{swap the a first and last letters of each word}
pwas eht a tirsf dna tasl setterl fo hace dorw
\S{SWAP THE A FIRST AND LAST LETTERS OF EACH WORD}
\end{document}
Explanation
TeX is a strange beast. Reading normal code and understanding it is a feat by itself. Understanding obfuscated TeX code goes a few steps further. I'll try to make this understandable for people who don't know TeX as well, so before we start here's a few concepts about TeX to make things easier to follow:
For (not so) absolute TeX beginners
First, and most important item in this list: the code does not have to be in rectangle shape, even though pop culture might lead you to think so.
TeX is a macro expansion language. You can, as an example, define \def\sayhello#1{Hello, #1!}
and then write \sayhello{Code Golfists}
to get TeX to print Hello, Code Golfists!
. This is called an “undelimited macro”, and to feed it the first (and only, in this case) parameter you enclose it in braces. TeX removes those braces when the macro grabs the argument. You can use up to 9 parameters: \def\say#1#2{#1, #2!}
then \say{Good news}{everyone}
.
The counterpart of undelimited macros are, unsurprisingly, delimited ones :) You could make the previous definition a tad more semantical: \def\say #1 to #2.{#1, #2!}
. In this case the parameters are followed by so-called parameter text. Such parameter text delimits the argument of the macro (#1
is delimited by ␣to␣
, spaces included, and #2
is delimited by .
). After that definition you can write \say Good news to everyone.
, which will expand to Good news, everyone!
. Nice, isn't it? :) However a delimited argument is (quoting the TeXbook) “the shortest (possibly empty) sequence of tokens with properly nested {...}
groups that is followed in the input by this particular list of non-parameter tokens”. This means that the expansion of \say Let's go to the mall to Martin
will produce a weird sentence. In this case you'd need to “hide” the first ␣to␣
with {...}
: \say {Let's go to the mall} to Martin
.
So far so good. Now things start to get weird. When TeX reads a character (which is defined by a “character code”), it assigns that character a “category code” (catcode, for friends :) which defines what that character will mean. This combination of character and category code makes a token (more on that here, for example). The ones which are of interest for us here are basically:
catcode 11, which define tokens which can make up a control sequence (a posh name for a macro). By default all letters [a-zA-Z] are catcode 11, so I can write \hello
, which is one single control sequence, while \he11o
is the control sequence \he
followed by two characters 1
, followed by the letter o
, because 1
is not catcode 11. If I did \catcode`1=11
, from that point on \he11o
would be one control sequence. One important thing is that catcodes are set when TeX first sees the character at hand, and such catcode is frozen... FOREVER! (terms and conditions may apply)
catcode 12, which are most of other characters, such as 0"!@*(?,.-+/
and so forth. They are the least special type of catcode as they serve only for writing stuff on the paper. But hey, who uses TeX for writing?!? (again, terms and conditions may apply)
catcode 13, which is hell :) Really. Stop reading and go do something out of your life. You don't want to know what catcode 13 is. Ever heard of Friday, 13th? Guess where it got its name from! Continue at your own risk! A catcode 13 character, also called an “active” character, is not just a character anymore, it is a macro itself! You can define it to have parameters and expand to something like we saw above. After you do \catcode`e=13
you think you can do \def e{I am the letter e!}
, BUT. YOU. CANNOT! e
is not a letter anymore, so \def
is not the \def
you know, it is \d e f
! Oh, choose another letter you say? Okay! \catcode`R=13 \def R{I am an ARRR!}
. Very well, Jimmy, try it! I dare you do that and write an R
in your code! That's what a catcode 13 is. I AM CALM! Let's move on.
Okay, now to grouping. This is fairly straightforward. Whatever assignments (\def
is an assignment operation, \let
(we'll get into it) is another) done in a group are restored to what they were before that group started unless that assignment is global. There are several ways to start groups, one of them is with catcode 1 and 2 characters (oh, catcodes again). By default {
is catcode 1, or begin-group, and }
is catcode 2, or end-group. An example: \def\a{1} \a{\def\a{2} \a} \a
This prints 1 2 1
. Outside the group \a
was 1
, then inside it was redefined to 2
, and when the group ended, it was restored to 1
.
The \let
operation is another assignment operation like \def
, but rather different. With \def
you define macros which will expand to stuff, with \let
you create copies of already existing things. After \let\blub=\def
(the =
is optional) you can change the start of the e
example from the catcode 13 item above to \blub e{...
and have fun with that one. Or better, instead of breaking stuff you can fix (would you look at that!) the R
example: \let\newr=R \catcode`R=13 \def R{I am an A\newr\newr\newr!}
. Quick question: could you rename to \newR
?
Finally, the so-called “spurious spaces”. This is kind of a taboo topic because there are people who claim that reputation earned in the TeX - LaTeX Stack Exchange by answering “spurious spaces” questions should not be considered, while others wholeheartedly disagree. Whom do you agree with? Place your bets! Meanwhile: TeX understands a line break as a space. Try to write several words with a line break (not an empty line) between them. Now add a %
at the end of these lines. It's like you were “commenting out” these end-of-line spaces. That's it :)
(Sort of) ungolfing the code
Let's make that rectangle into something (arguably) easier to follow:
{
\let~\catcode
~`A13
\defA#1{~`#113\gdef}
AGG#1{~`#113\global\let}
GFF\else
GHH\fi
AQQ{Q}
AII{\ifxQ}
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
ADD#1#2#3|{I#2FE{#1}#2#3|H}
ACC#1#2|{D{}#2Q|#1 }
ABBH#1 {HI#1FC#1|BH}
\gdef\S#1{\iftrueBH#1 Q }
}
Explanation of each step
each line contains one single instruction. Let's go one by one, dissecting them:
{
First we start a group to keep some changes (namely catcode changes) local so that they don't mess up the input text.
\let~\catcode
Basically all TeX obfuscation codes start with this instruction. By default, both in plain TeX and LaTeX, the ~
character is the one active character which can be made into a macro for further use. And the best tool for weirdifying TeX code are catcode changes, so this is generally the best choice. Now instead of \catcode`A=13
we can write ~`A13
(the =
is optional):
~`A13
Now the letter A
is an active character, and we can define it to do something:
\defA#1{~`#113\gdef}
A
is now a macro that takes one argument (which should be another character). First the catcode of the argument is changed to 13 to make it active: ~`#113
(replace the ~
by \catcode
and add an =
and you have: \catcode`#1=13
). Finally it leaves a \gdef
(global \def
) in the input stream. In short, A
makes another character active and start its definition. Let's try it:
AGG#1{~`#113\global\let}
AG
first “activates” G
and does \gdef
, which followed by the next G
starts the definition. The definition of G
is very similar to that of A
, except that instead of \gdef
it does a \global\let
(there isn't a \glet
like the \gdef
). In short, G
activates a character and makes it be something else. Let's make shortcuts for two commands we'll use later:
GFF\else
GHH\fi
Now instead of \else
and \fi
we can simply use F
and H
. Much shorter :)
AQQ{Q}
Now we use A
again to define another macro, Q
. The above statement basically does (in a less obfuscated language) \def\Q{\Q}
. This isn't a terribly interesting definition, but it has an interesting feature. Unless you do want to break some code, the only macro that expands to Q
is Q
itself, so it acts like a unique marker (it's called a quark). You can use the \ifx
conditional to test if the argument of a macro is such quark with \ifx Q#1
:
AII{\ifxQ}
so you can be pretty sure that you found such a marker. Notice that in this definition I removed the space between \ifx
and Q
. Usually this would lead to an error (note that the syntax highlight thinks that \ifxQ
is one thing), but since now Q
is catcode 13 it cannot form a control sequence. Be careful, however, not to expand this quark or you'll get stuck in an infinite loop because Q
expands to Q
which expands to Q
which...
Now that the preliminaries are done, we can go to the proper algorithm to pwas eht setterl. Due to TeX's tokenization the algorithm has to be written backwards. This is because at the time you do a definition TeX will tokenize (assign catcodes) to the characters in the definition using the current settings so, for example, if I do:
\def\one{E}
\catcode`E=13\def E{1}
\one E
the output is E1
, whereas if I change the order of the definitions:
\catcode`E=13\def E{1}
\def\one{E}
\one E
the output is 11
. This is because in the first example the E
in the definition was tokenized as a letter (catcode 11) before the catcode change, so it will always be a letter E
. In the second example, however, E
was first made active, and only then \one
was defined, and now the definition contains the catcode 13 E
which expands to 1
.
I will, however, overlook this fact and reorder the definitions to have a logical (but not working) order. In the following paragraphs you can assume that the letters B
, C
, D
, and E
are active.
\gdef\S#1{\iftrueBH#1 Q }
(notice there was a small bug in the previous version, it did not contain the final space in the definition above. I only noticed it while writing this. Read on and you'll see why we need that one to properly terminate the macro.)
First we define the user-level macro, \S
. This one shouldn't be an active character to have a friendly (?) syntax, so the macro for gwappins eht setterl is \S
. The macro starts with an always-true conditional \iftrue
(it will soon be clear why), and then calls the B
macro followed by H
(which we defined earlier to be \fi
) to match the \iftrue
. Then we leave the argument of the macro #1
followed by a space and by the quark Q
. Suppose we use \S{hello world}
, then the input stream should look like this: \iftrue BHhello world Q␣
(I replaced the last space by a ␣
so that the rendering of the site does not eat it, like I did in the previous version of the code). \iftrue
is true, so it expands and we are left with BHhello world Q␣
. TeX does not remove the \fi
(H
) after the conditional is evaluated, instead it leaves it there until the \fi
is actually expanded. Now the B
macro is expanded:
ABBH#1 {HI#1FC#1|BH}
B
is a delimited macro whose parameter text is H#1␣
, so the argument is whatever is between H
and a space. Continuing the example above the input stream prior to the expansion of B
is BHhello world Q␣
. B
is followed by H
, as it should (otherwise TeX would raise an error), then the next space is between hello
and world
, so #1
is the word hello
. And here we got to split the input text at the spaces. Yay :D The expansion of B
removes everything up to the first space from the input stream and replaces by HI#1FC#1|BH
with #1
being hello
: HIhelloFChello|BHworld Q␣
. Notice that there is a new BH
later in the input stream, to do a tail recursion of B
and process later words. After this word is processed B
processes the next word until the word-to-be-processed is the quark Q
. The last space after Q
is needed because the delimited macro B
requires one at the end of the argument. With the previous version (see edit history) the code would misbehave if you used \S{hello world}abc abc
(the space between the abc
s would vanish).
OK, back to the input stream: HIhelloFChello|BHworld Q␣
. First there's the H
(\fi
) that completes the initial \iftrue
. Now we have this (pseudocoded):
I
hello
F
Chello|B
H
world Q␣
The I...F...H
think is actually a \ifx Q...\else...\fi
structure. The \ifx
test checks if the (first token of the) word grabbed is the Q
quark. If it is there is nothing else to do and the execution terminates, otherwise what remains is: Chello|BHworld Q␣
. Now C
is expanded:
ACC#1#2|{D#2Q|#1 }
The first argument of C
is undelimited, so unless braced it will be a single token, The second argument is delimited by |
, so after the expansion of C
(with #1=h
and #2=ello
) the input stream is: DelloQ|h BHworld Q␣
. Notice that another |
is put there, and the h
of hello
is put after that. Half the swapping is done; the first letter is at the end. In TeX it is easy to grab the first token of a token list. A simple macro \def\first#1#2|{#1}
gets the first letter when you use \first hello|
. The last one is a problem because TeX always grabs the “smallest, possibly empty” token list as argument, so we need a few work-arounds. Next item in the token list is D
:
ADD#1#2|{I#1FE{}#1#2|H}
This D
macro is one of the work-arounds and it's useful in the sole case where the word has a single letter. Suppose instead of hello
we had x
. In this case the input stream would be DQ|x
, then D
would expand (with #1=Q
, and #2
empty) to: IQFE{}Q|Hx
. This is similar to the I...F...H
(\ifx Q...\else...\fi
) block in B
, which will see that the argument is the quark and will interrupt the execution leaving only x
for typesetting. In other cases (returning to the hello
example), D
would expand (with #1=e
and #2=lloQ
) to: IeFE{}elloQ|Hh BHworld Q␣
. Again, the I...F...H
will check for Q
but will fail and take the \else
branch: E{}elloQ|Hh BHworld Q␣
. Now the last piece of this thing, the E
macro would expand:
AEE#1#2#3|{I#3#2#1FE{#1#2}#3|H}
The parameter text here is quite similar to C
and D
; the first and second arguments are undelimited, and the last one is delimited by |
. The input stream looks like this: E{}elloQ|Hh BHworld Q␣
, then E
expands (with #1
empty, #2=e
, and #3=lloQ
): IlloQeFE{e}lloQ|HHh BHworld Q␣
. Another I...F...H
block checks for the quark (which sees l
and returns false
): E{e}lloQ|HHh BHworld Q␣
. Now E
expands again (with #1=e
empty, #2=l
, and #3=loQ
): IloQleFE{el}loQ|HHHh BHworld Q␣
. And again I...F...H
. The macro does a few more iterations until the Q
is finally found and the true
branch is taken: E{el}loQ|HHHh BHworld Q␣
-> IoQlelFE{ell}oQ|HHHHh BHworld Q␣
-> E{ell}oQ|HHHHh BHworld Q␣
-> IQoellFE{ello}Q|HHHHHh BHworld Q␣
. Now the quark is found and the conditional expands to: oellHHHHh BHworld Q␣
. Phew.
Oh, wait, what are these? NORMAL LETTERS? Oh, boy! The letters are finally found and TeX writes down oell
, then a bunch of H
(\fi
) are found and expanded (to nothing) leaving the input stream with: oellh BHworld Q␣
. Now the first word has the first and last letters swapped and what TeX finds next is the other B
to repeat the whole process for the next word.
}
Finally we end the group started back there so that all local assignments are undone. The local assignments are the catcode changes of the letters A
, B
, C
, ... which were made macros so that they return to their normal letter meaning and can be safely used in the text. And that's it. Now the \S
macro defined back there will trigger the processing of the text as above.
One interesting thing about this code is that it is fully expandable. That is, you can safely use it in moving arguments without worrying that it will explode. You can even use the code to check if the last letter of a word is the same as the second (for whatever reason you would need that) in an \if
test:
\if\S{here} true\else false\fi % prints true (plus junk, which you would need to handle)
\if\S{test} true\else false\fi % prints false
Sorry for the (probably far too) wordy explanation. I tried to make it as clear as possible for non TeXies as well :)
Summary for the impatient
The macro \S
prepends the input with an active character B
which grabs lists of tokens delimited by a final space and passes them to C
. C
takes the first token in that list and moves it to the end of the token list and expands D
with what remains. D
checks if “what remains” is empty, in which case a single-letter word was found, then do nothing; otherwise expands E
. E
loops through the token list until it finds the last letter in the word, when it is found it leaves that last letter, followed by the middle of the word, which is then followed by the first letter left at the end of the token stream by C
.
Hello, world!
becomes,elloH !orldw
(swapping punctuation as a letter) oroellH, dorlw!
(keeping punctuation in place)? \$\endgroup\$