TeX - LaTeX Asked on August 23, 2021
In the following code I would like to "hack" a^b
suchas to apply macro{a}{b}
instead. The solution should work with natural exponent and letters eventually indexed like in {abcd}^4
or {x_1}^2
for example.
documentclass{article}
usepackage{forloop}
newcounter{power}
newcommandmacro[2]{%
forloop[1]{power}{0}{value{power} < #2}{#1kern0.3ex}%
kern-0.3ex%
}
newcommandtest[1]{
% Lost in translation...
}
begin{document}
test{x} % ---> x
test{x y} % ---> x y
test{x y^2} % ---> x y y
test{x^3 y^2 z} % ---> x x x y y z
end{document}
The code should be self-explaining:
documentclass{article}
usepackage{xparse}
ExplSyntaxOn
NewDocumentCommand{test}{m}
{
projetmbc_test:n { #1 }
}
tl_new:N l__projetmbc_test_tl
cs_new_protected:Nn projetmbc_test:n
{
tl_set:Nn l__projetmbc_test_tl { #1 }
regex_replace_all:nnN
{ (cB. .*? cE.|[[:alpha:]])^ } % search a braced group or single letter followed by ^
{ c{projetmbc_power:nn} 1 } % prepend projetmbc_power:nn and remove ^
l__projetmbc_test_tl
ensuremath { tl_use:N l__projetmbc_test_tl }
}
cs_new:Nn projetmbc_power:nn
{
prg_replicate:nn { #2 } { #1 }
}
ExplSyntaxOff
begin{document}
test{x} % ---> x
test{x y} % ---> x y
test{x y^2} % ---> x y y
test{x^3 y^2 z} % ---> x x x y y z
test{{x_1}^3 {abcde}^2}
end{document}
Correct answer by egreg on August 23, 2021
Here's a LuaLaTeX-based solution.
% !TEX TS-program = lualatex
documentclass{article}
usepackage{amsmath} % for 'ensuremath' macro
usepackage{luacode} % for 'luacode' env. and 'luastringN' macro
begin{luacode}
function test ( s )
s = s:gsub ( "(%a+) ^(%d+)", string.rep ) -- e.g., 'alpha^3'
s = s:gsub ( "(%a)^(%d+)" , string.rep ) -- e.g., 'x^2'
s = s:gsub ( "(%b{})^(%d+)" , string.rep ) -- e.g., '{x_1}^4'
tex.sprint ( s )
end
end{luacode}
% Define a LaTeX wrapper macro:
newcommandtest[1]{directlua{test(luastringN{#1})}}
begin{document}
obeylines
test{$x$}
test{$x y$}
test{$x^1 y^12$}
test{$x^3 y^2 z$}
test{${x_1}^3 {abcde}^2$} % courtesy of @egreg's posting
test{$alpha^2lambda^3omega^4$}
end{document}
Answered by Mico on August 23, 2021
You can also do it fully-expandably in expl3
by absorbing tokens one-by-one. This ignores spaces, but since you are planning to use this for partial derivatives in math mode, that shouldn't be a problem. It might be quite slow, though. It also doesn't work recursively, i.e. test{{x^3}}
will not be repeated.
documentclass{article}
usepackage{xparse}
ExplSyntaxOn
cs_new:Npn mbc_process_powers:w #1 #2 #3 {
str_if_eq:nnF { #1 } { q_stop }
{
str_if_eq:nnTF { #2 } { ^ }
{
prg_replicate:nn { #3 } { #1 }
mbc_process_powers:w
} {
#1 mbc_process_powers:w { #2 } { #3 }
}
}
}
NewExpandableDocumentCommand test { m }
{
mbc_process_powers:w #1 q_stop q_stop q_stop
}
ExplSyntaxOff
begin{document}
ttfamily % nicer font for meaning
edefx{test{x}} meaningx % ---> x
edefx{test{x y}} meaningx % ---> xy
edefx{test{x y^2}} meaningx % ---> xyy
edefx{test{x^3 y^2 z}} meaningx % ---> xxxyyz
edefx{test{{abcd}^4 or {x_1}^3}} meaningx % ---> abcdabcdabcdabcdorx_1x_1x_1
end{document}
If you can't or don't want to use expl3
, you can also implement it in normal LaTeX, but you'll need a few helper macros:
makeatletter
protecteddef@qstop{@qstop}
ifdefineddirectlua
% LuaTeX doesn't have pdfstrcmp.
directlua{
function pdfstrcmp(a, b)
if a < b then
tex.sprint("-1")
elseif a > b then
tex.sprint("1")
else
tex.sprint("0")
end
end
}
longdefpdfstrcmp#1#2{directlua{pdfstrcmp("luaescapestring{#1}", "luaescapestring{#2}")}}
fi
def@ifstrequal#1#2{%
ifnumpdfstrcmp{unexpanded{#1}}{unexpanded{#2}}=0
expandafter@firstoftwo
else
expandafter@secondoftwo
fi
}
defreplicate#1#2{%
ifnumnumexpr#1relax>0
#2%
expandafterreplicateexpandafter{numbernumexpr(#1)-1relax}{#2}%
fi
}
defprocesspowers#1#2#3{%
@ifstrequal{#1}{@qstop}{}{%
@ifstrequal{#2}{^}{%
replicate{#3}{#1}%
processpowers
}{%
#1processpowers{#2}{#3}%
}%
}%
}
newcommandtest[1]{processpowers#1@qstop@qstop@qstop}
makeatother
Answered by Henri Menke on August 23, 2021
In the comments you asked whether LuaTeX can also work with TeX tokens. Instead of asking for you to explain the use-case, I will consider this as academic interest and provide an example showcasing how to do this in principle.
LuaTeX comes with the built-in token
library which provides facilities to work with an manipulate TeX tokens. In particular it has the function scan_toks()
which allows to scan a list tokens delimited by balanced braces.
directlua{t = token.scan_toks()}{...}
After this call the variable t
will contain whatever is in ...
. Tokens are represented as Lua tables and you can query the token's properties as elements of said table. The elements that I use here are
cmdname
The name of the internal TeX command that the token representstok
The unique token identifier that TeX assignsTo compare whether two tokens are the same you can compare their tok
properties (although care must be taken when comparing control sequences this way, since they can have additional properties such as protected
, long
or outer
).
Finally we can put tokens back into the input stream using token.put_next
(which also accepts a table in which case it simple traverses the table and puts each token into the input stream).
In the example I do not wrap directlua
but I define a luacall
by putting the Lua function definition into the global function table retrieved using lua.get_functions_table()
and subsequently registering the luacall
in TeX using token.set_lua
. This has some benefits which are not really relevant here but nice to have, e.g. the test
macro defined this way expands in a single step.
One big annoyance with this solution is that LuaTeX tokens do not “know” what input they originate from, i.e. to check whether a token contains a number we have to compare it with all tokens that result in a number as well. For this I defined a lookup table which maps the tok
property of the tokens to the corresponding numbers.
documentclass{article}
usepackage{luacode}
begin{luacode}
-- Lookup table to convert tokens to numbers
local numbers = {
[token.create(string.byte("1")).tok] = 1,
[token.create(string.byte("2")).tok] = 2,
[token.create(string.byte("3")).tok] = 3,
[token.create(string.byte("4")).tok] = 4,
[token.create(string.byte("5")).tok] = 5,
[token.create(string.byte("6")).tok] = 6,
[token.create(string.byte("7")).tok] = 7,
[token.create(string.byte("8")).tok] = 8,
[token.create(string.byte("9")).tok] = 9,
[token.create(string.byte("0")).tok] = 0,
}
-- Register a new Lua function with TeX
local lft = lua.get_functions_table()
lft[#lft + 1] = function()
-- Scan a list of tokens delimited by balanced braces
local toks = token.scan_toks()
local result = {}
local stack = {}
local currentgrouplevel = 0
local n = 1
while n <= #toks do
-- We have to scan balanced braces, so we in/decrease the
-- currentgrouplevel on every brace
if toks[n].cmdname == "left_brace" then
currentgrouplevel = currentgrouplevel + 1
elseif toks[n].cmdname == "right_brace" then
currentgrouplevel = currentgrouplevel - 1
end
-- Collect tokens on a stack
table.insert(stack, toks[n])
-- If we are not inside braces, check for the ^
if currentgrouplevel == 0 then
if toks[n + 1] and toks[n + 1].cmdname == "sup_mark" and
toks[n + 2] and toks[n + 2].cmdname == "other_char" then
-- Convert the token right after ^ to a number by looking it up
local rep = assert(numbers[toks[n + 2].tok], "Token is not a number")
-- Append the stack to the result rep times
for i = 1, rep do
for _, t in ipairs(stack) do
table.insert(result, t)
end
end
-- Flush the stack
stack = {}
-- Skip the next two tokens (^ and number)
n = n + 2
else
-- We are not inside braces but there is also no ^, so we flush the stack
for _, t in ipairs(stack) do
table.insert(result, t)
end
stack = {}
end
end
-- Move on to the next token
n = n + 1
end
-- Flush whatever is still on the stack
for _, t in ipairs(stack) do
table.insert(result, t)
end
-- Put the result back into the input stream
token.put_next(result)
end
-- Bind the registered function to "test"
token.set_lua("test", #lft, "global")
-- The "global" definition (similar to gdef) is needed because the luacode*
-- environment is an implicit TeX group and set_lua obey TeX grouping
end{luacode}
begin{document}
test{$x$}
test{$x y$}
test{$x^1 y^12$}
test{$x^3 y^2 z$}
test{${x_1}^3 {a{bc}de}^2$}
test{${x_1}^3$}
test{$alpha^2lambda^3omega^4$}
end{document}
Answered by Henri Menke on August 23, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP