lua を使用して tex ファイルを書き換える方法

lua を使用して tex ファイルを書き換える方法

$数式内のと$$を使用して、構造ツリーへの追加を含め、これらの数式の自動タグ付けを実行するという問題を解決しようとしています。 または を使用したバリアントは、たとえば を使用しようとするとエラーが発生するため、機能しません\grabinline。最近、 を介して独自のファイルを操作できることを読みました。 最初は、前に文字がない場合は とを単純に置き換えようとしましたが、機能しないだけでなく、ファイルを書き換えようとするとファイルが破損し、新しい奇妙な行が追加されます。\grabdisplayequationlualatex$$$\

助けてください。この問題、つまりファイルの破損の問題を解決するにはどうすればいいですか。また、なぜこのようなことが起こるのか説明してください。

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()

答え1

この問題は TeX に関連するものではなく、同じファイルの読み取りと書き込みに関する一般的な問題です。それで、何が起こっているのでしょうか?

おそらく得られる出力は次のようになります

\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$

これは、元のファイルを開いた後、最初の行の最初のバイトにいるために発生します。次に、 を使用してlines、最初の行を読み取ります: \documentclass{article}。その後、行内の位置は 2 行目の先頭になります。ここで を発行するとf:write、変更されていない最初の行がファイルの現在の位置、つまり 2 行目に書き込まれます。これにより、既存のコンテンツが上書きされます。

つまり、ファイルには次の内容が含まれます。

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

そして、現在の位置は2番目の の終わりです\documentclass{article}。次のlines反復では、行の残りを読み取り、 を取得しますt.lua")}。次に、3行目の先頭にいて、読み取ったテキストで上書きします。

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

観察したファイルが得られるまで、この処理が各行ごとに繰り返されます。

ここで学べる教訓は、ファイルを読み取っている最中にそのファイルを上書きしないことです。

対照的に、他のプログラミング言語には、readlines一見すると Lua の に似ている関数 (Python の など) が含まれていますio.lines。しかし、たとえば Python の はreadlinesすべての行を直接配列に読み込みますが、Lua はループの各反復で一度に 1 行しか読み込みません。そのため、同様のコードでそれほど明白な問題は発生しません。これは、読み取りと書き込みが明確に分離されているためです (読み取りは の間だけ行われreadline、書き込みは の後で行われます)。

いずれにしても、TeX の実行中に TeX ソース ファイルを書き換えることは非常に危険であり、互換性もありません (開いているファイルを編集する場合、Windows は賢明です)。

process_input_buffer一度に 1 行だけ変更したい場合は、より安全で簡単に使用できます。status.input_ptr特定のファイルの行のみに影響を与えるには、次のようにします (このプロセスでパターンを修正しました)。

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")

たとえ危険性がはるかに低くなったとしても、入力行を書き換えずに目標を達成する方法を探すことをお勧めします。

答え2

(少し遅れましたが、それでも役に立つと思います)

これは、新しいテキストファイルを作成しない解決策です。代わりに、本質的にはプリプロセッサのように「オンザフライ」で動作し、一致する と のペアをそれぞれ LaTeX 推奨の表現に置き換えます$$$これは次のように行われます。前にTeX はマクロの展開などの通常の作業を開始します。

入力 put のオンザフライで変更された内容を書き出すための新しい出力テキスト ファイルの作成は、別の演習として残されています。

これを概念実証段階から実際の作業に十分な堅牢性を持たせるには、プリプロセッサによって検査される素材が逐語的モードであるか、URL 文字列に含まれているかをテストするコードも追加する必要があります。このような場合や同様の場合には、置換は実行すべきではありませんよね?

ここに画像の説明を入力してください

% !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}

関連情報