In case you are not all too familiar with the programming-paradigms underlying TeX I try to give a rough outline:
When TeX processes a .tex-input-file, the content of that file is taken for a set of directives for creating so-called tokens and appending them to the token-stream.
E.g., if the .tex-input-file contains This is a \macro{with argument} in \LaTeX.
, TeX by and by, as needed, forms the following tokens for having them ready for further processing:
Explicit character token T
of category 11(letter): T11
Explicit character token h
of category 11(letter): h11
Explicit character token i
of category 11(letter): i11
Explicit character token s
of category 11(letter): s11
Explicit space character token of category 10(space)—␣
shall denote a space, i.e., the character whose code-point-number both in ASCIII and in unicode is 32: ␣10
Explicit character token i
of category 11(letter): i11
Explicit character token s
of category 11(letter): s11
Explicit space character token of category 10(space): ␣10
Explicit character token a
of category 11(letter): a11
Explicit space character token of category 10(space): ␣10
Control word token \macro
:
\macro
Explicit character token {
of category 1(begin group):
{1
Explicit character token w
of category 11(letter): w11
Explicit character token i
of category 11(letter): i11
Explicit character token t
of category 11(letter): t11
Explicit character token h
of category 11(letter): h11
Explicit space character token of category 10(space): ␣10
Explicit character token a
of category 11(letter): a11
Explicit character token r
of category 11(letter): r11
Explicit character token g
of category 11(letter): g11
Explicit character token u
of category 11(letter): u11
Explicit character token m
of category 11(letter): m11
Explicit character token e
of category 11(letter): e11
Explicit character token n
of category 11(letter): n11
Explicit character token t
of category 11(letter): t11
Explicit character token }
of category 2(end group):
}2
Explicit space character token of category 10(space): ␣10
Explicit character token i
of category 11(letter): i11
Explicit character token n
of category 11(letter): n11
Explicit space character token of category 10(space): ␣10
Control word token \LaTeX
: \LaTeX
Explicit character token .
of category 12(other): .12
I.e., by and by, as needed, you get the following tokens appended the token-stream:
T11h11i11s11␣10i11s11␣10a11␣10\macro{1w11i11t11h11␣10a11r11g11u11m11e11n11t11}2␣10i11n11␣10\LaTeX.12.
So tokens can be seen as nice little items placed one behind another and thus forming a token-queue/a token-stream of nice little items. Most of the processing in TeX deals with such tokens.
The concept of expansion in TeX is about obtaining such nice little token-items in ways other than by just "looking" at the .tex-input-file and forming tokens according to what is written there. Tokens in TeX can come into being by reading and tokenizing .tex-input. Tokens can also come into being in the course of expansion.
The concept of macro-programming in TeX is about having TeX's "expansion-station" remove token-items that currently are defined to be macros from the token stream and grabbing sets of token-items from the token stream, serving as macro-arguments, and in the token stream substituting the removed token-items by another set of tokens which is formed by the those tokens which at the time of defining the macro formed the ⟨definition⟩'s ⟨replacement text⟩, but with the ⟨replacement text⟩'s parameters substituted by the tokens that were grabbed as arguments.
So macro-programming in (La)TeX is about flipping these nice little token-items in the token-stream around and thus modifying the order of tokens in the token-stream.
TeX's "token-producing-station" bit by bit only produces that many tokens as are needed for subsequent stations to be able to do their next thing.
So when TeX's "token-producing-station" produced a token and appended it to the token-stream to be transported to subsequent "stations" of processing, the first station where that token goes is the "expansion-station". If the "expansion-station" finds that this token is expandable, e.g., is a macro-token, the "expansion-station" kindly asks the "token-producing-station" to produce and send sets of tokens that are suitable for forming the macro-token's arguments. And no more. Then expansion takes place, where stuff gets replaced. With the resulting set of tokens, where stuff is replaced, if the "expansion-station" finds the first token to be expandable, expansion of that token takes place in the same way. If the "expansion-station" finds that token not to be expandable, that token is send on to reach subsequent "stations" of processing. So expansion can be described as a "regurgitative process".
In subsequent "stations" tokens are looked at and work is done which is not related to expansion. E.g., in subsequent "stations" tokens may be taken for directives for defining macros, opening up or closing local scopes(!), creating boxes/doing typesetting-work for producing a page of the document, writing messages to the screen or to the .log-file, writing external text-files, adding s.th. to the .dvi- or .pdf-file, etc.
If subsequent "stations" need more tokens for being able to do their work, they require them to be delivered from the "expansion-station". The "expansion-station" in turn requires from the "token-producing-station", does expansion and delivers the resulting tokens.
That's why you can, e.g., "say" \hbox\macro
after having "said" \def\macro{{stuff}}
.
\hbox
in the token-producing-station yields the control-word-token \hbox. That happens to be an unexpandable primitive and thus goes through the expansion-station without being substituted/removed. When it arrives in the station where horizontal boxes are produced, the "horizontal-box-producing-station" requires more tokens. So the token-producing-station produces the token \macro. This is expanded in the "expansion-station" and thus replaced by the tokens {1s11t11u11f11f11}2.
Each of these tokens is not expandabe, so the "expansion-station" does not do whatsoever substitution to any of them but leaves them in place so they all make it into the "horizontal-box-producing-station" where now all in all tokens forming the directive \hbox{1s11t11u11f11f11}2 have have arrived and the work of producing a \hbox
is done.
But beforehand there was the directive \def\macro{{stuff}}
. With that directive, the "token-producing-station" produces the token \def and sends it towards the "expansion-station". The expansion-station finds it to be an unexpandable primitive and sends it on. When that token reaches the "macro-defining-station", the "macro-defining-station" requires the "expansion-station" to deliver more tokens, but without expansion. So the "expansion-station" requires the "token-producing-station" to produce and deliver more tokens. The "token-producing-station" produces and delivers the tokens {1{1s11t11u11f11f11}2}2 and the "expansion-station" sends them untouched towards subsequent stations so that they reach the "macro-defining-station".
(With \edef
instead of \def
the "macro-defining-station" would not require the "expansion station" to deliver tokens without expanding them, but would allow the "expansion station" to do its work as usual.)
After this attempt at a rough outline, let's look at the issues mentioned in your question:
- If not in
\ExplSyntaxOn
/expl3-mode, spaces in your code do matter in many places and you need to be picky about that and you may need to use %
here and there for preventing the coming into being of a space token.
- With TeX distributions which are up to date and where primitives
\expanded
and \unexpanded
are available, you can combine \expanded
and \unexpanded
to have macros defined via .store
-handler toplevel-expanded in order to obtain the tokens that form their ⟨replacement text⟩s. (By the way: As it is .initial:n
with :n
coming from expl3-syntax, I wonder why it is not .store:N
.)
- During expansion of a macro a single hash-character-token of category 6(parameter) (#6) occurring in the macro's ⟨definition⟩'s ⟨replacement text⟩ trailed by one of the digit-character-tokens of category 12(other) 112, 212, 312, 412, 512, 612, 712, 812, 912, is taken for a parameter which is to be substituted by a macro argument. But two consecutive hash-character-tokens of category 6(parameter) (#6#6) occurring in the ⟨replacement text⟩ just collapse into a single one which is not taken for a parameter-thingie, but, like other tokens of the ⟨replacement text⟩, is inserted into the token-stream as is. Thus hashes that shall go into the ⟨replacement text⟩ of the macro
\thmstyleheadcmd
in a way where they are not taken for parameter-thingies of the ⟨replacement text⟩ but are delivered as is, need to be doubled. The doubling of hashes within the ⟨replacement text⟩ of a ⟨definition⟩ can be achieved via combining \edef
with \unexpanded
as in \edef\macro{\unexpanded{At the time of expanding, the following yields a single hash character token of category 6(parameter): #}}
.
\documentclass{article}
\usepackage{amsthm,kantlipsum}
\DeclareKeys[thmstyle]{%
spaceabove .store = \thmstylespaceabove,
spaceabove .initial:n = \topsep,
spacebelow .store = \thmstylespacebelow,
spacebelow .initial:n = \topsep,
bodyfont .store = \thmstylebodyfont,
bodyfont .initial:n = \itshape,
headindent .store = \thmstyleheadindent,
headindent .initial:n = 0pt,
headfont .store = \thmstyleheadfont,
headfont .initial:n = \bfseries,
headpunct .store = \thmstyleheadpunct,
headpunct .initial:n = {.},
postheadspace .store = \thmstylepostheadspace,
postheadspace .initial:n = 5pt plus 1pt minus 1pt,
% Let's define \thmstyleheadcmd in a way where hashes going
% into the replacement text are doubled and thus not taken for
% parameters of the macro
headstyle .code = \edef\thmstyleheadcmd{\unexpanded{#1}},
headstyle .initial:n = {\thmname{#1}\thmnumber{ #2}\thmnote{ #3}},
}
\newcommand{\NewThmStyle}[2]{%
\begingroup
% \show\thmstyleheadcmd
\SetKeys[thmstyle]{#2}%
% \show\thmstyleheadcmd
\expanded{%
\endgroup
% \show\noexpand\thmstyleheadcmd
\unexpanded{\newtheoremstyle{#1}}%
\unexpanded\expandafter{\expandafter{\thmstylespaceabove}}%
\unexpanded\expandafter{\expandafter{\thmstylespacebelow}}%
\unexpanded\expandafter{\expandafter{\thmstylebodyfont}}%
\unexpanded\expandafter{\expandafter{\thmstyleheadindent}}%
\unexpanded\expandafter{\expandafter{\thmstyleheadfont}}%
\unexpanded\expandafter{\expandafter{\thmstyleheadpunct}}%
\unexpanded\expandafter{\expandafter{\thmstylepostheadspace}}%
\unexpanded\expandafter{\expandafter{\thmstyleheadcmd}}%
}%
}
\NewThmStyle{teststyle}{headstyle=Something\thmname{#1}Something\thmnumber{ #2}Something\thmnote{ #3}}
\newtheorem{theorem}{Theorem}
\theoremstyle{teststyle}
\newtheorem{test}{Test}
\begin{document}
\begin{theorem}
\kant[2][1]
\end{theorem}
\begin{test}
\kant[2][1]
\end{test}
\begin{test}[Note]
\kant[2][1]
\end{test}
\end{document}
If you happen to work with a TeX distribution where \DeclareKeys
is available while for some obscure reason the underlying TeX-engine does not provide \expanded
or \unexpanded
, which is very very very unlikely, you can - instead of combining \edef
and \unexpanded
- combine \edef
with \the
-expansion of a token-register for achieving hash doubling. Instead of combining \expanded
with \unexpanded
for getting the tokens that form the toplevel-expansions of the macros defined by the keyval-interface before closing the group, you can combine toplevel-expanding via \expandafter
with successively exchanging arguments holding the tokens that form the result of toplevel-expansion until brace-groups enclosing results of toplevel-expansion of all macros and the call to \newtheoremstyle
are placed behind the token \endgroup
:
\documentclass{article}
\usepackage{amsthm,kantlipsum}
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newtoks\Scratchtoks
\DeclareKeys[thmstyle]{%
spaceabove .store = \thmstylespaceabove,
spaceabove .initial:n = \topsep,
spacebelow .store = \thmstylespacebelow,
spacebelow .initial:n = \topsep,
bodyfont .store = \thmstylebodyfont,
bodyfont .initial:n = \itshape,
headindent .store = \thmstyleheadindent,
headindent .initial:n = 0pt,
headfont .store = \thmstyleheadfont,
headfont .initial:n = \bfseries,
headpunct .store = \thmstyleheadpunct,
headpunct .initial:n = {.},
postheadspace .store = \thmstylepostheadspace,
postheadspace .initial:n = 5pt plus 1pt minus 1pt,
headstyle .code = \expandafter\PassFirstToSecond\expandafter{\the\Scratchtoks}{%
\Scratchtoks={#1}\edef\thmstyleheadcmd{\the\Scratchtoks}\Scratchtoks=%
},
headstyle .initial:n = {\thmname{#1}\thmnumber{ #2}\thmnote{ #3}},
}
\newcommand{\NewThmStyle}[2]{%
\begingroup
%\show\thmstyleheadcmd
\SetKeys[thmstyle]{#2}%
\expandafter\PassFirstToSecond\expandafter{\thmstyleheadcmd}{% 1
\expandafter\PassFirstToSecond\expandafter{\thmstylepostheadspace}{% 2
\expandafter\PassFirstToSecond\expandafter{\thmstyleheadpunct}{% 3
\expandafter\PassFirstToSecond\expandafter{\thmstyleheadfont}{% 4
\expandafter\PassFirstToSecond\expandafter{\thmstyleheadindent}{% 5
\expandafter\PassFirstToSecond\expandafter{\thmstylebodyfont}{% 6
\expandafter\PassFirstToSecond\expandafter{\thmstylespacebelow}{% 7
\expandafter\PassFirstToSecond\expandafter{\thmstylespaceabove}{% 8
%\show\thmstyleheadcmd
\endgroup
%\show\thmstyleheadcmd
\newtheoremstyle{#1}%
}}}}}}}}% 87654321
}
\NewThmStyle{teststyle}{headstyle=Something\thmname{#1}Something\thmnumber{ #2}Something\thmnote{ #3}}
\newtheorem{theorem}{Theorem}
\theoremstyle{teststyle}
\newtheorem{test}{Test}
\begin{document}
\begin{theorem}
\kant[2][1]
\end{theorem}
\begin{test}
\kant[2][1]
\end{test}
\begin{test}[Note]
\kant[2][1]
\end{test}
\end{document}
\begingroup
and\endgroup
, the definition is no longer ignored.