LuaTeX では、Lua コード中で tex.count、tex.dimen、tex.skip といった(仮想的な)配列を用いて、 TeX のレジスタにアクセスすることができる。
\newcount\countA \newdimen\dimenA \newskip\skipA \directlua{ tex.count.countA = 42 % \countA=42 と同じ tex.dimen.dimenA = 100 * 0x10000 % sp単位の整数で指定 % glue_specノードを作る local gsp = node.new(node.id("glue_spec")) gsp.width = 3 * 0x10000 % sp単位 tex.skip.skipA = gsp } \message{^^J% countA=\the\countA^^J% dimenA=\the\dimenA^^J% skipA=\the\skipA^^J% } \bye
寸法(dimen)レジスタやグルー(skip)レジスタが保持するのは長さである。上の例で解るように、寸法(伸縮なしの長さ)は sp 単位の整数として表し、また、グルー(伸縮付きの長さ)は glue_spec というノードオブジェクトを用いて表される(glue_spec gsp について、gsp.width がその自然長である)。
これらのアクセスは TeX における代入文と同じ性質をもち、グループ内で値の設定を行った場合はその効果は局所的である。
\newdimen\dimenA \dimenA=50pt \begingroup % グループに入ってLuaで値を設定 \message{^^J\the\dimenA} %% \dimenA=100pt と同値 \directlua{ tex.dimen.dimenA = 100 * 0x10000 } \message{^^J\the\dimenA} \endgroup % グループを抜けると以前の値に戻る \message{^^J\the\dimenA^^J} \bye
50.0pt 100.0pt 50.0pt
……のはずである。ところが、グルー値の場合は何故か代入がローカルにならない。
\newskip\skipA \skipA=50pt \begingroup \message{^^J\the\skipA} %% ローカル代入 \skipA=100pt を行いたいが…… \directlua{ local gsp = node.new(node.id("glue_spec")) gsp.width = 100 * 0x10000 tex.skip.skipA = gsp } \message{^^J\the\skipA} \endgroup % グループを抜けると…… \message{^^J\the\skipA^^J} \bye
50.0pt 100.0pt 100.0pt ←戻っていない
ところが、Lua での代入の前に、グループの中で一度 TeX で代入を行っておくと、想定通りにグループを抜けると以前の値に復帰する。
\newskip\skipA \newskip\skipB \skipA=50pt \begingroup \skipA=60pt % グループ中で代入 \message{^^J\the\skipA} \directlua{ gsp = node.new(node.id("glue_spec")) gsp.width = 100 * 0x10000 tex.skip.skipA = gsp } \message{^^J\the\skipA} \endgroup \message{^^J\the\skipA^^J} \skipB=1pt \directlua{ print(gsp.width) } \bye
60.0pt 100.0pt 50.0pt ←戻った 65536
果たしてこの挙動は想定仕様なのだろうか、それともバグなのだろうか。悩ましい……。
少し違う話。上のコードでは最後に gsp.width
の値を出力させているが、その結果は「65536」(1pt に相当)になっている。gsp.width
に代入した値は 6553600(100pt)のはずなのでこれは奇妙である。
推測であるが、次のような理由が考えられる。glue_spec は TeX のノードの一種なので、Lua の動的メモリ管理ではなく TeX のそれの下に存在する。もし、skipA への Lua での代入がないとすると、skipA がもっていた「ローカルな値」である「60pt を示す glue_spec」はグループを抜けると無効になるので、そのノードは開放される。ここで、グループを抜ける前に Lua で skipA の指すノードを Lua 変数 gsp の指すものに変えたとすると、「60pt」の代わりに gsp のノードが開放されてしまうのであろう。後に、TeX で「\skipB=1pt
」を実行すると、「1pt の glue_spec」が生成されるが、その際に、今や無効になった gsp のノードが再利用されてそこに値「1pt」が設定されたため、gsp.width
の値が変わってしまったのであろう。
こちらの挙動は筋は通っている。でも、Lua のグローバル変数の指す値が TeX の「既に無効なオブジェクト」であるという状態は非常に危険である。Lua でグルーレジスタを扱うのは難しそうだ。
[追記]グループを二重にしてみると、全く不可解な挙動になる。
\newskip\skipA %\tracingassigns=1 \tracingrestores=1 \tracingonline=1 \skipA=25pt \begingroup \skipA=50pt \begingroup \directlua{ local gsp = node.new(node.id("glue_spec")) gsp.width = 100 * 0x10000 tex.skip.skipA = gsp } \message{^^J\the\skipA (Level \the\currentgrouplevel)} \endgroup % グループを抜けると…… \message{^^J\the\skipA (Level \the\currentgrouplevel)} \endgroup \message{^^J\the\skipA (Level \the\currentgrouplevel)^^J} \bye
100.0pt(Level 2) 0.0pt(Level 1) 25.0pt(Level 0)
最外で 25pt を代入し、グループ内で 50pt と 100pt を代入しているのに、Lua で代入したグループから抜けた後は、何故か値がゼロになってしまっている。これはどう考えてもバグのようだ……。