TransWikia.com

a^b ---> macro{a}{b}

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}

4 Answers

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}

enter image description here

Correct answer by egreg on August 23, 2021

Here's a LuaLaTeX-based solution.

enter image description here

% !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 represents
  • tok The unique token identifier that TeX assigns

To 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}

enter image description here

Answered by Henri Menke on August 23, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP