This is a simple replacement tool at a Lua level. Everything written in \parseme
is a target for replacements, but the TeX commands are not expanded and we can hide a portion of text from replacement procedure. The replacements are stored in a simple Lua table, the first column contains the searched terms and the second column contains their replacements. Testing goes from top to bottom. For instance A
is replaced quite soon and never again. On the other hand, X
is going to be Y
, then Y
goes to Z
and that goes to A
. The name of commands will be replaced, e.g. \colorme
would become \cojuicerme
. I used the \clrme
command to illustrate a fast workaround, if needed.
If you need even more advanced tool for string manipulations than the string
library can offer, I can recommend you LPeg
library, that's a tool which is already installed in LuaTeX. Some examples are mentioned in Programming in LuaTeX article.
If you run my example you will see 1 2 3 4 5 6
three times in the terminal. It means there were 6 replacement tests called three times by the \parseme
command during typesetting.
%! lualatex mal-text-parser.tex
\documentclass[a4paper]{article}
\pagestyle{empty}
\parindent=0pt
% It fixes beginnings and endings of I/O lines, among other things...
\usepackage{luatextra}
\pagestyle{empty}
\usepackage{luacode}
\usepackage{xcolor}
\begin{document}
\def\parseme#1{% TeX definition...
\directlua{parseme("\unexpanded{#1}")}%
% This is working: "\unexpanded{#1}" but one must write \\ instead of backslash in the \parseme command.
}% End of \parseme command...
\begin{luacode*}
-- A conversion table, from -> to
local maltable={
{"la", "beer"},
{"lo", "juice"},
{"A", "Hello World! I was here! Phantom-as!"},
{"X","Y"},
{"Y","Z"},
{"Z","A"},
}
function parseme(text) -- Lua function
-- Read an argument sent by TeX...
content=text -- Backup of original text...
-- Do all the necessary replacements...
for i=1,#maltable do
tex.sprint("\\message{"..i.."}")
content=string.gsub(content,maltable[i][1],maltable[i][2])
end
-- Typeset the result at the terminal and to the document...
print(content)
tex.print(content)
end
\end{luacode*}
\def\formated{\textbf{lalo}} % This part is not replaced.
\def\clrme#1{{\color{red}#1}} % Definition of the command.
% We use \clrme instead of \colorme because we would get -> \cojuicerme as lo is being replaced...
% lalo in \formated is protected from expansion and replacement...
Text of the paragraph. \parseme{My long \\formated{} sentence. \\clrme{To la red!} \\textbf{Hey!} Ending. lalala lo lo lo A}\par
End of the paragraph. \parseme{My \\clrme{next} try.} The end.\par
Input is X, result is: \parseme{X}; X goes to Y to Z to A, but A is not replaced anymore.
\end{document}
You can "smuggle" definitions out of their group with the TeX primitive \aftergroup
. I'll first explain what \aftergroup
does, then give a possible definition of \smuggleone
using \aftergroup
and finally apply it to your MWE.
The short answer is that you could define \smuggleone
(I've removed "out" from the name) as
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
If you paste in this definition and replace \smuggleoutone#1
by \smuggleone#2
in your MWE it should work. (Note that you were passing the wrong argument to \smuggleoutone
, it should have been #2
instead of #1
.)
About \aftergroup
:
It is possible to insert a single token right after the end of the current group using \aftergroup<token>
.
You can only smuggle out one token at a time, so if you want to move out something consisting of multiple tokens (like a definition) you'll need to \aftergroup
each of these tokens separately. This includes things like braces ({}
), so for instance
{\aftergroup\def\aftergroup\abc\aftergroup{\aftergroup A\aftergroup B\aftergroup C\aftergroup}}
is equivalent to {}\def\abc{ABC}
.
This is quite a hassle, so the following may be more practical:
{\gdef\somethingunique{\def\abc{ABC}}\aftergroup\somethingunique}
This works by globally assigning \def\abc{ABC}
to \somethingunique
and inserting that after the end of the group.
If ABC is replaced by some macro, say \ABC
, that is only defined within the current group and that you want to be fully expanded then you'll want to use \xdef
instead:
{%
\newcommand*\ABC{ABC}%
\xdef\somethingunique{\def\noexpand\abc{\ABC}}%
\aftergroup\somethingunique
}
I've inserted \noexpand
in front of \abc
because we don't want \abc
to be expanded.
If you only want \ABC
to be expanded once you can instead use the slightly more complicated
{
\newcommand*\ABC{\somethingthatshouldntbeexpanded}%
\xdef\somethingunique{\def\noexpand\abc{\unexpanded\expandafter{\ABC}}}%
\aftergroup\somethingunique
}
(The primitives \noexpand
, \unexpanded
and \expandafter
are all explained in this this answer.)
To smuggle the definition of \abc
out of a group you can do what I just did above with \ABC
replaced by \abc
itself.
That way \abc
will be defined as itself (expanded once) immediately after the end of the group.
There's also \AfterGroup
from the etextools
package.
It acts mostly like \aftergroup
, but it takes an argument that can consist of any number of tokens.
So, for instance, \Aftergroup{\def\abc{ABC}}
inserts \def\abc{ABC}
after the current group without all of the aforementioned hassle.
There's also a starred version, \Aftergroup*
, that does the same thing but first expands its arguments fully.
Don't use the etextools
package though! It is apparently buggy and no longer maintained and it is incompatible with a bunch of other packages. (Thanks to Ulrike Fischer for pointing that out, here are a few examples: 1, 2, 3, 4.)
Even though you shouldn't use the package, \AfterGroup
itself can be quite useful. It is defined as follows:
\makeatletter %% <- make @ usable in command names
\newcount\ettl@fter
\newrobustcmd\AfterGroup{\@ifstar{\ettl@AfterGroup\@firstofone}{\ettl@AfterGroup\unexpanded}}
\newrobustcmd\ettl@AfterGroup[2]{%
\csxdef{ettl@fterGroup\number\numexpr\the\ettl@fter+1}%
{\global\csundef{ettl@fterGroup\number\numexpr\the\ettl@fter+1}#1{#2}}%
\global\advance\ettl@fter\@ne
\expandafter\aftergroup\csname ettl@fterGroup\the\ettl@fter\endcsname}
\makeatother %% <- revert @
Defining \smuggleone
:
To smuggle a macro that was already defined past the end of a group, it may be more effective to use \let
instead of \def
.
One advantage is that it will also works for macros with arguments:
{
\newcommand*\abc[1]{``#1''}%
\global\let\somethingunique\abc
\aftergroup\let\aftergroup\abc\aftergroup\somethingunique
}
\abc{This works!}
This leads us to a possible definition of \smuggleone
.
\documentclass{article}
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
\begin{document}
\newcommand*\abc[1]{\textbf{#1}}%
{%
{%
\renewcommand*\abc[1]{``#1''}%
\smuggleone\abc
\abc{Local definition}
}\par
\abc{Local definition}
}\par
\abc{Global definition}
\end{document}
The reason for the use of a counter here is that if you use \somethingunique
every time you're smuggling something, it won't really be unique.
Whenever multiple smuggling operations are happening consescutively, because you're using \smuggleone
multiple times from within the same group or from a group contained in another one where \smuggleone
is used, this will cause trouble.
The above command therefore creates \smuggle@<n>
the <n>
-th time it is used.
This can be made more efficient (memory-wise) by reusing these command sequences as much as possible, as in jfbu's answer.
All of this applied to your MWE:
Here is your MWE with two changes: (1) I've added the definition of \smuggleone
and (2) I've replaced %\smuggleoutone#1
by \smuggleone#2
.
\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calc}
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
\begin{document}
\begin{tikzpicture}[globalize/.code n args={2}{\xdef#2{#1}},
localize/.code n args={2}{\pgfmathsetmacro{#2}{#1}\typeout{#2}
\smuggleone#2
}]
\begin{scope}[local bounding box=extra]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
\pgfextra{\xdef\myangle{\n1}};
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (extra.north) {using \verb|\pgfextra|};
%
\begin{scope}[local bounding box=globalize,xshift=3cm]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
[globalize={\n1}{\myangle}];
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (globalize.north) {using \texttt{globalize}};
%
\xdef\myangle{7}
\begin{scope}[local bounding box=localize,xshift=6cm]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
[localize={\n1}{\myangle}];
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (localize.north) {attempt to smuggle};
%
\end{tikzpicture}
\end{document}
\node[anchor=south] at (globalize.north) {using \texttt{globalize}};
%
\xdef\myangle{7}
\begin{scope}[local bounding box=localize,xshift=6cm]
\path let \p1=($(2,1)-(0,0)$),\n1={atan2(\y1,\x1)} in
[localize={\n1}{\myangle}];
\node at (1,0) {\myangle};
\end{scope}
\node[anchor=south] at (localize.north) {attempt to smuggle};
%
\end{tikzpicture}
\end{document}
Addendum
Here's a \smuggle
macro that works up to depth 10. It doesn't let you smuggle anything across eleven borders because 10
is two tokens (yeah, that's a stupid reason).
I could make it work for any depth, but I like how short the definition currently is and it seems unlikely that any sane person would need this.
The syntax is \smuggle[<depth>]{<macro>}
, and the default <depth>
is 1
.
It works by calling \smuggleone
and then also \aftergroup
ing \smuggle[<depth-1>]{<macro>}
.
\documentclass{article}
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
\DeclareRobustCommand\smuggle[2][1]{%
\smuggleone{#2}%
\ifnum#1>1
\aftergroup\smuggle\aftergroup[\expandafter\aftergroup\the\numexpr#1-1\aftergroup]\aftergroup#2%
\fi
}
\begin{document}
\newcommand*\abc[1]{\textbf{#1}}
{%
{%
{%
\renewcommand*\abc[1]{``#1''}%
\smuggle[2]{\abc}%
Definition at depth 3: \abc{Local definition}
}\par
Definition of depth 2: \abc{Local definition}
}\par
Definition of depth 1: \abc{Local definition}
}\par
Definition at depth 0: \abc{Global definition}
\end{document}
Best Answer
The author is referring to a series of commands he has defined and that are part od the file
Commons.sty
(the file can be found following a link in the site you linked to in your question); they basically are shortcuts allowing you to write fractions and matrices with or without delimiters; in the following document I've extracted the definitions fromCommons.sty
and provide an example of their use:Of course, if you save the file Commons.sty in a convenient place (your local tree, for example) where TeX can find it, you can load the package and directly use the commands, as in:
As a side note, I would have defined
\pmat
and\bmat
using directlybmatrix
andpmatrix
as provided byamsmath
.