Como reescrever arquivo tex usando lua

Como reescrever arquivo tex usando lua

Tento resolver o problema com $e $$em fórmulas, para realizar a marcação automática dessas fórmulas, inclusive adicionando na árvore de estrutura. A variante com \grabinlineor \grabdisplaynão funciona para mim porque recebo um erro ao tentar usar equation, por exemplo. Recentemente li que posso trabalhar com meus próprios arquivos via lualatex. Primeiro tentei simplesmente substituir $$e $se não houver nenhum \caractere antes, mas além de não funcionar, meu arquivo fica corrompido quando tento reescrevê-lo, quer dizer, adiciona novas linhas estranhas.

Por favor me ajude, como posso resolver esse problema, quero dizer, o problema com a corrupção do arquivo, e por favor me explique por que isso acontece?

test.tex

\documentclass{article}
\directlua{require("test.lua")}
\pagestyle{empty}
\begin{document}
test
$$a+b=a^b$$ $a-b=a/b$
\end{document}

test.lua

f=io.open(tex.jobname..".tex","r+")
lines=f:lines()
for line in lines do
line=line:gsub("([^\\]?)$$(.+-)$$","%1\\[%2\\]")
line=line:gsub("([^\\]?)$(.+-)$","%1\\(%2\\)")
texio.write_nl("line of file "..line)
f:write(line)
end
f:close()

Responder1

O problema não está relacionado ao TeX, é um problema geral de leitura e gravação do mesmo arquivo. Então, o que está acontecendo?

A saída que você provavelmente obterá é

\documentclass{article}
\documentclass{article}t.lua")}
t.lua")}le{empty}
le{empty}cument}
cument}a+b=a^b$$ $a-b=a/b$
a+b=a^b$$ $a-b=a/b$

Isso acontece porque após abrir o arquivo original, você está no primeiro byte da primeira linha. Então usando lines, você lê a primeira linha: \documentclass{article}. Depois disso, sua posição na linha será o início da segunda linha. Aqui você emite f:write, para que a primeira linha não modificada seja gravada na posição atual no arquivo, que é a segunda linha. Ele substitui o conteúdo existente.

Então agora, o arquivo contém:

\documentclass{article}
\documentclass{article}t.lua")}
\pagestyle{empty}
\begin{document}
test
$$a+b=a^b$$ $a-b=a/b$
\end{document}

e sua posição atual está no final do segundo \documentclass{article}. Agora sua próxima linesiteração lê o resto da linha, então você obtém t.lua")}. Então você está no início da terceira linha e a substitui pelo texto que acabou de ler, então você obtém

documentclass{article}
\documentclass{article}t.lua")}
t.lua")}le{empty}
\begin{document}
test
$$a+b=a^b$$ $a-b=a/b$
\end{document}

Isso se repete para cada linha, até obter o arquivo observado.

Que lição você pode aprender aqui: Não sobrescreva um arquivo enquanto ainda estiver lendo ele!

Em contraste, algumas outras linguagens de programação contêm funções, como Python com readlines, que à primeira vista parecem semelhantes às de Lua io.lines. Mas, por exemplo, o Python readlineslê diretamente todas as linhas em um array, enquanto Lua lê apenas uma linha por vez em cada iteração do loop. Portanto, nenhum problema tão óbvio aparece com código semelhante porque a leitura e a escrita estão claramente separadas: lendo apenas durante readline, escrevendo depois.

De qualquer forma, reescrever seu arquivo fonte TeX durante a execução do TeX é muito perigoso e também não é compatível (o Windows é sensato quando se trata de editar arquivos abertos).

É muito mais seguro e fácil de usar process_input_bufferse você quiser alterar apenas uma linha por vez. Você pode usar status.input_ptrpara afetar apenas linhas de arquivos específicos: (corrigi o padrão no processo)

luatexbase.add_to_callback("process_input_buffer", function(line)
  if status.input_ptr ~= 1 then return end -- Only change lines of the main file
  texio.write_nl("line of file "..line)
  print(line:match("([^\\]?)$$(.-)$%$"))
  return line:gsub("([^\\]?)$$(.-)$%$","%1\\[%2\\]")
             :gsub("([^\\]?)$(.-)%$","%1\\(%2\\)")
end, "my_math_rewrite")

Mesmo que isso seja muito menos perigoso, eu ainda recomendaria procurar maneiras de arquivar seus objetivos sem reescrever as linhas de entrada.

Responder2

(um pouco atrasado para a festa, mas espero que ainda seja útil)

Aqui está uma solução que não cria um novo arquivo de texto. Em vez disso, ele funciona "on the fly", essencialmente como um pré-processador, e substitui os pares correspondentes de $$e $, respectivamente, por suas expressões preferidas pelo LaTeX: Isso aconteceantesTeX inicia seu trabalho habitual de expansão de macros, etc.

A criação de um novo arquivo de texto de saída, para escrever o conteúdo modificado instantaneamente da entrada de entrada, é deixada como um exercício separado.

Para levar isso do estágio de prova de conceito para algo que possa ser suficientemente robusto para o trabalho no mundo real, certamente também seria necessário adicionar código para testar se o material que está sendo examinado pelo pré-processador acontece no modo literal ou está contido em uma string de URL. Nestes e em casos semelhantes, nenhuma substituição deve ser realizada, certo?

insira a descrição da imagem aqui

% !TEX TS-program = lualatex

%% Create external file to store Lua code:
\RequirePackage{filecontents}
\begin{filecontents*}{test.lua}
function substitute_dollar_symbols ( line )
    line = line:gsub ( "([^\\]?)$$(.+-)$$" , "%1\\[%2\\]" )
    line = line:gsub ( "([^\\]?)$(.+-)$"   , "%1\\(%2\\)" )
    return line
end
\end{filecontents*}

\documentclass{article}
%% Load Lua code from external file:
\directlua{require("test.lua")}

%% Create two LaTeX utility macros to (a) assign Lua 
%% function to the "process_input_buffer" callback 
%% and (b) remove the function from that callback:
\newcommand\SubstituteDollarSymbolsOn{%
  \directlua{luatexbase.add_to_callback (
    "process_input_buffer", 
    substitute_dollar_symbols , 
    "substitute_dollar_symbols" )}}
\newcommand\SubstituteDollarSymbolsOff{%
  \directlua{luatexbase.remove_from_callback (
    "process_input_buffer",
    "substitute_dollar_symbols" )}}

%% Activate the Lua function at start of document:
\AtBeginDocument{\SubstituteDollarSymbolsOn}

\begin{document}
test
$$a+b=a^b$$ $a-b=a/b$ % $$ $
abc $$uvw$$
\end{document}

informação relacionada