Como posso fazer macros chamadas de tex.print tail-recursive?

Como posso fazer macros chamadas de tex.print tail-recursive?

Considerar

\documentclass[12pt]{article}
\usepackage{luacode}
%\usepackage{luacr}
%\usepackage{miscellaneous}
\begin{document}

\global\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    if i<30 then
        i=i+1
        tex.sprint(i .. [[, \F]])
    end
end
\end{luacode*}
\F

\end{document}

Problema:

TeX capacity exceeded, sorry [text input levels=15].

Pelo que posso ver tex.sprinté "equivalente" a um \inputque contém o conteúdo impresso, podendo ter \input15 níveis no máximo.

Pergunta:

  • por que oeliminação de recursão de caudanão funciona quando a entrada é de uma linha em vez de uma lista de tokens?
  • como corrigir o problema? Supondo que eu precise tex.sprint(), por exemplo, imprimir mais algum conteúdo de string à direita de \F.

enquanto isso encontro uma solução alternativa usando token.put_next(), coloco um \relax e pego depois.

Responder1

Você pode forçar o tex a desenrolar a pilha de entrada antes de recorrer, olhando para frente com\expandafter

insira a descrição da imagem aqui

\documentclass[12pt]{article}

%\usepackage{luacr}
%\usepackage{miscellaneous}
\begin{document}

\def\F{\expanded{\noexpand\directlua{f()}\expandafter}}
\directlua{
i=0
function f()
    if i<30 then
        i=i+1
        tex.sprint(i .. [[, \string\F]])
    end
end
}
\F

\end{document}

Responder2

0.texio.closeinput()

Sugerido porum comentário

Código:


%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
\begin{document}

\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    if i~=0 then
        texio.closeinput()
    end
    if i<20000 then
        i=i+1
        tex.sprint(i .. [[, \F]])
    end
end
\end{luacode*}
\F

\end{document}

O manual explica...

Esta função deve ser usada com cuidado. Ele atua como \endinput mas no final de Lua. Você pode usá-lo para (mais ou menos) forçar um salto de volta para o TeX.

o que significa que ele elimina o conteúdo restante do "arquivo" superior.

Nota/esclarecimento do manual,

  • isso não acontecerealmentevolte para o TeX, o seguinte conteúdo Lua ainda é executado
  • tex.print()comandos anteriores também são descartados
  • ao contrário \endinputdo seguinte conteúdo na linha é descartado
  • masse houver alguns tokens pendentes colocados, token.put_next()eles serão mantidos enquanto aqueles depois dele
  • aplica-se apenas ao "arquivo"/pseudoarquivo real em vez de, por exemplo, lista de tokens de argumento

1. Use token.put_next no token de continuação

%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
\begin{document}

\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    if i<20000 then
        i=i+1
        tex.sprint(i .. [[, ]])
        token.put_next(token.create("F"))
    end
end
\end{luacode*}
\F

\end{document}

(experimentalmente, token.put_next()afinal tudo vem tex.*print(), independente da ordem no código)

Eu acho que a razão pela qual esse método funciona é que ao expandir uma macro, ao contrário de quando token.get_next()ou token.scan_toks()etc. é executado, o TeX desenrola a pilha de entrada (mesmo no caso de a macro não ter nenhum argumento como neste caso)

2. Use futurelet

%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
%\usepackage{miscellaneous}
%\tracingmacros=1
\begin{document}

\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    if i<20000 then
        i=i+1
        tex.sprint(i .. [[, \immediateassignment\futurelet\a\F]])
    end
end
\end{luacode*}
\expanded{\F}
\end{document}

\immediateassignmentusado para fazê-lo funcionar em contexto somente de expansão.

Observe que se o token a seguir for um notexpandedtoken, ele será alterado.

3. (funciona apenas parcialmente, não use) Use token.put_next em outro token e get_next de dentro de Lua

%! TEX program = lualatex
\documentclass[12pt]{article}
\usepackage{luacode}
%\usepackage{miscellaneous}
%\tracingmacros=1
\begin{document}

\def\F{\directlua{f()}}
\begin{luacode*}
i=0
function f()
    token.get_next()  -- the relax token, either the original one or result of put_next
    if i<3000 then
        i=i+1
        tex.sprint(i .. [[, \F]])
        token.put_next(token.create("relax"))
    end
end
\end{luacode*}
\F\relax
\end{document}

Isso não faz a recursividade final adequada (de alguma forma, token.get_next()não elimina o nível de entrada se estiver esgotado); portanto, se 3000 for aumentado para um valor maior, você verá

TeX capacity exceeded, sorry [input stack size=5000].

informação relacionada