LISP on TeX v2.0リリースノート

はじめに

10/25,LISP on TeXのv2.0(CTAN: Package lisp-on-tex)をリリースした.簡単に今回のリリース内容をまとめておく.

READMEの更新

READMEをMarkdown形式に移行した*1.結果,CTANのページ上からREADME.mdが見れるようになっているはずである.

ごみ集め機構(GC)の実装

通常のTeXプログラミングと違い,LISP on TeXでは内部で大量の制御綴りをCONSセルとして使用する.そのため,大量に資源消費するプログラムを作成すると,「! TeX capacity exceeded, sorry」のメッセージとともにTeXの実行そのものが終了してしまう問題があった.その対策として,LISP on TeXにごみ集め機構(GC)を実装した.使用するためには,markGCオプションを使用する.

\usepackage[markGC]{lisp-on-tex}

これでGCが有効になる.これにより,未使用になったCONSセルが再利用されるようになる.
デフォルトでGCはオフになっている.これは,GCを有効にすることによりアロケーション時間が1.5倍かかるようになってしまうのと,実際にGCが必要な場面の方が少ないためである.

なお,GCopt={heapsize=n}というオプションを追加で与えると,ヒープサイズをnにすることができる.指定しない場合は32768が指定されたものとみなす.

エラー出力の強化

以前のリリースでone-shot continuationを実装したことにより,LISP on TeXでは内部的に例外を扱えるようになっていた.そこで,今回のリリースではエラー出力を強化した.例えば.

\lispinterp{(\define 'aaa' :42)}

とすると,

! LISP on TeX ERROR: The 1st argument of \define or \defineM must be a symbol o  
r valid list..  

と出力される.ほかにも引数の個数チェックなどが追加されている.

\defineなどの省略記法

これは,LISP on TeXの初期の課題であった,(\define (\foo \x) ...)の形式を認めるようにする変更である.正直,LISP on TeXにはマクロがあるので,そちらで実装してもいいのだが,速度を考えてTeX側で実装してある.ちなみに,これを実装する最大の動機がこちら.

だいぶ時間をあけてしまった.

l3regexのラッパー

文字列,すなわちTeXのトークン列処理の強化として,LISP on TeX正規表現を導入した.ただし,正規表現エンジンを自前で実装するのはさすがに困難なので,既存のTeX上の正規表現エンジンであるl3regexを使用することにした.

使い方は簡単で,追加のパッケージlisp-mod-l3regexを読み込むだけである.

\usepackage{lisp-mod-l3regex}

これで,いくつかの関数が使用可能になる.例えば

\lispinterp{(\regReplaceAll '(\w+?)to(\w+?)' '$\1\c{to}\2$' 'AtoB BtoC')}

とすると,文字列

'$A\to B$ $B\to C$'

が得られる.正規表現そのものの記法についてはl3regexのドキュメント*2を参考にしてほしい.
また,導入される関数については,LISP on TeXのREADME.mdに記載があるのでそちらを確認してほしい.置換,分割,パターンマッチの関数をそれぞれ用意してある.

*1:本当はLaTeXにしようと思っていたのだが,思ったより時間をとることができず,Markdown形式になった

*2:texdoc l3regexで見られるはず

もしTeX芸人が遠藤侑介の『あなたの知らない超絶技巧プログラミングの世界』を読んだら

Quine書きますよね。

というわけで、TeXでQuineを書く話。ただし、前例(TeX de Quine!! - 0番染色体)にあるように、dviやpdfに書き出す例は多いみたいなので、私はコンソール出力結果がQuineになるようにしてみた。コードはこちら。

gist.github.com

これをquine.texとし、

max_print_line=10000 etex -no-shell-escape quine.tex

なんていう風に実行すると、そのコンソール出力がソースコードと一致する。これを書くための技法を紹介しよう。

基本的なQuineの書き方

まずは『あなたの知らない超絶技巧プログラミングの世界』(Amazon.co.jp: あなたの知らない超絶技巧プログラミングの世界: 遠藤 侑介: 本)を読もう。その第3章の技術を使っただけである。なお、その技術をTeXで再現するに際し、e-TeX拡張の\unexpandedを用いて展開制御している*1

TeXでやるにあたっての工夫

TeXのコンソール出力は使いづらいことで有名である[要出典]。まず、バージョン情報や読み込んだファイル、実行結果のサイズなどを勝手に出力する。しかも抑制できない。また、長い文字列を出そうとすると勝手に改行する。これらを解決する必要があった。

ユーザの意図しない出力への対応

まずは、ユーザの意図しない出力への対応である。今回のQuineではdviやpdf側の出力は一切気にする必要がないので,どんな文字列が出力されようが問題ないだろう,と思う読者もいるかもしれない.しかし,それは大きな間違いである.etexを起動したときの文字列を見てもらいたい.

$ etex
This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014/W32TeX) (preloaded format=etex)
 restricted \write18 enabled.
**

TeXエンジンのバージョン情報などとともに,restricted \write18が有効になっている旨の出力がある.この「\write18」という文字列がQuineでは邪魔になる.そのままソースコードにこれを書くとエラーになるためである.これを回避するために,いささか邪道ではあるが-no-shell-escapeオプションをつけて回避した.\write18さえ禁止してしまえば,出力は

$ etex -no-shell-escape
This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014/W32TeX) (preloaded format=etex)

となり,無害な出力が得られる.なお,この出力を見てもらえばわかるが,TeXエンジンの情報が入るので,Quineのソースコードは実行するTeXエンジンに(バージョンなども含めて)依存する.

長い出力の強制改行対策

この問題を認識してもらうために,次のコードを考える.

\message{wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww}\bye

これをsample.texとして実行してみる.

$ etex sample
This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014/W32TeX) (preloaded format=etex)
 restricted \write18 enabled.
entering extended mode
(./sample.tex
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
wwwwwwwwwwwwwwwww )
No pages of output.
Transcript written on sample.log.

なぜか改行される.これは,このツイート


にあるように,TeXの設定ファイルで規定されるものらしい.TeX側からこれを変更するのはつらい.そこで,その設定自体は環境変数を使えば動的に書き換えられることを利用し,これも邪道ではあるが,max_print_line=10000と環境変数を設定して実行することにより,これを回避した.

まとめ

TeXのコンソール出力は扱いづらい.

*1:別に使わなくてもできる

Windows PhoneでテザリングしつつICSするために

数か月ぶりの記事はTeX関係ないという.

はじめに

Windows 10にしてから数日が経った.Hyper-Vを有効にしてもスリープできるようになったと聞き,喜々としてICSでNATもどきして楽しもうした.うまくいっているようだった.しばらくして,持っているWindows Phoneでそのマシンをテザリングして外につないだ.なぜか,ホスト側のWifiに与えられているIPアドレスと,ICSで共有している仮想ネットワークのNICに与えられているIPアドレスが同じだった.別に問題ないけども気持ち悪い.

問題

Windows Phone(試した実機はMADOSMA)でテザリングしたとき,ICSしているWifiアダプタ(試した実機はSurface Pro 3)に割り当てられるIPアドレスと,ICSのローカル側(Hyper-Vの内部ネットワーク)に与えられるIPアドレスが同一になる場合がある.

原因

テザリングで割り振られるアドレスの範囲と,ICSで振られるIPアドレスの範囲が同じ192.168.137.0/24になっている.

対処法

ICSで振られるIPアドレスの範囲を変更する.レジストリ「HKLM\System\CurrentControlSet\services\SharedAccess\Parameters」の

  • (REG_SZ) ScopeAddress
  • (REG_SZ) StandaloneDhcpAddress

の値を,192.168.x.1に変更する.サブネットマスクは255.155.255.0固定で変更できない.


参考
https://support.microsoft.com/en-us/kb/230148

LaTeXでマクロの引数をオーバーロード

はじめに

TeX Forumにこんな質問が来ていた.

TeXにおける制御綴の引数の個数について

要は,TeXのマクロを関数っぽいものとみなし,その上で同名のマクロに引数の個数が異なるものをオーバーロードさせたいというものである.TeXのマクロは単なる字句の置き換え規則に過ぎない*1ので,これを実現するにはTeX言語沼に入らないといけない*2

先の記事の場合,要件は1引数および2引数の場合のみだったが,今後,複数の場合でオーバーロードしたい場合があるかもしれない.単に,そのようなマクロを定義するためのマクロをつくるのが面白そうなので作ってみた.

スタイルファイルの紹介

公開場所

今回,スタイルファイルをoverload.styとして,hak7a3 / overload — Bitbucketに公開しておいた.

使い方

overload.styの使い方は,非常に簡単である.まず,\usepackageでoverload.styを読み込む.

\usepackage{overload}

次に,\newoverloadでオーバーロードするコントロールシーケンスを登録する.

\newoverload{\foo}

ここでは,\fooを「これから複数の引数に対応するマクロ」として登録している.最後に,各引数の個数に対応する動作を\addoverloadで登録する.\addoverloadは\newoverloadで登録したコントロールシーケンス,引数の個数,およびマクロの置き換えテキストを引数として受け取り,オーバーロード用のマクロを作成する.

\addoverload{\foo}{2}{(2):#1 and #2}
\addoverload{\foo}{1}{(1):#1 only}
\addoverload{\foo}{0}{(0):orz}

この例では,上から順に2引数,1引数,および引数がない場合の動作を登録している*3

引数をオーバーロードするように定義したマクロは,引数の末尾を示す\endinvokeを末尾に付加して呼び出す必要がある.例を示す.

\foo\endinvoke

\foo{bar}{baz}\endinvoke

\foo{bar}\endinvoke

上から順に,引数なし,2引数,および1引数呼び出しに対応する,先に述べたように,TeXのマクロは単純なテキストの置き換えしかできないので,そもそも引数の扱いが普通の言語と異なる.それを回避する方法として,今回は末尾を明記する手法を用いた.

これを実行すると,次の結果を得ることができる.
f:id:hak7a3:20150524201435p:plain
引数の個数に従って,マクロの動作が変化していることがわかる.

overload.styで提供されるマクロ展開部分は完全展開可能にしてあるので,\edefの内部でも利用可能である.実際,overload.styのテストコードでそのような使い方をしている.

制限

overload.styで定義したマクロの呼び出しには,いくつか制限がある.大きなものとしては,

  1. 各引数の先頭に\endinvokeが出現してはいけない
  2. 各引数にバランスの取れていない\fiが存在してはいけない

が挙げられる.これらの問題は引数の末尾か否かの判定処理のためのものである.完全展開可能にするため,どうしてもこのようにするしかなく,現状で解決の糸口はつかめていない.

また,内部でeTeX拡張を使っているので,古いTeX処理系では動作しない.もっとも,現在使用されているほとんどのTeXエンジンがeTeX拡張を認めているため,この制限に引っかかる可能性は低いだろう.

*1:これは上記の記事の回答にも示されている

*2:ようこそ

*3:一応注意しておくと,定義順は自由である

TeXで色々な無限ループ

小ネタ.思うところがあったのでいくつか試してみた.

自身が再帰的に展開されていく例

TeXで無限ループやる場合は大抵これ[要出典].実装法はいくつかある.

単純なマクロ
\documentclass{article}
\begin{document}
\newcommand\hoge{\hoge}
\hoge
\end{document}

よくやる.

\aftergroup
\documentclass{article}
\begin{document}
\newcommand\hoge{{\aftergroup\hoge}}
\hoge
\end{document}

\aftergroupにより,\hogeが波閉じ括弧(グループの終了)の直後に挿入される.結構使う.

\afterassignment
\documentclass{article}
\begin{document}
\newcount\fuga
\newcommand\hoge{\afterassignment\hoge\fuga=0 }
\hoge
\end{document}

\aftergroupと同様に,代入の終了時に\hogeが挿入される.

垂直モードに移行しない\par

\documentclass{article}
\begin{document}
\def\par{}
hoge
\end{document}

どこで見たか思い出せないけど,割と面白い例.TeXは\parを挿入することで,垂直モードに移行しようとする.オリジナルの\parには垂直モードに移行する機能があるが,この例では,\defすることによってその機能を奪っている.結果,垂直モードに移行できない.で,水平モードのままなのでTeXが\parを挿入し,……を無限に繰り返す.なぜこんな仕様になってしまったんだ.

追記: あった
水平モードにおける垂直モード用グルーと\par

TeXだってテスト書きたい(続)

前回は,LaTeX単体テストを書くためのスタイルファイルであるqstest.styを紹介したが,今回はそれをJenkinsと連携させる方法を述べる.

Jenkinsからqstest.styの出力結果は直接読み取れない.テスト結果ならJUnitなどが吐き出すXML文書と同様の形式である必要がある.しかし,qstest.styを書き換えるのはもっと面倒である.なので,

  1. latexでqstest.styの形式で記述されたテストコードを処理し
  2. 結果をJUnitが吐き出すXML文書の形式に変換する

Pythonスクリプトを書いた.ここに公開している.変換自体は単純で,特筆することはないので省略.jenkins-qstest.pyがスクリプト本体で,次のように利用する.

python3 jenkins-qstest.py example.tex

実行するとexample.xmlができるので,これをテスト結果としてJenkinsに読ませればOK.標準でlatexコマンドを使用するようになっているが,その辺はオプションでコントロール可能にしてある.

次のテストファイ

\documentclass{article}
\usepackage{qstest}
\IncludeTests{*}
\LogTests{lgout}{*}{*}
\begin{document}
\begin{qstest}
\begin{qstest}{Example}{label}
 \Expect*{\the\numexpr1+2}{3}
\end{qstest}
\begin{qstest}{Example-fail}{label}
 \Expect*{\numexpr1+2}{3}
\end{qstest}
\end{qstest}
\end{document}

で実行した結果がこちら.



やったぜ.

ちなみに,これはLISP on TeXの今後の開発で利用するために作成したスクリプトで,実際に運用を開始している.

pdfTeXじゃないけどGhostscriptさえあれば画像は埋め込めるよねっ

2015/1/24 16:48 GhostscriptをGhostScriptと記述していました.お詫び申し上げます.

はじめに

こんなことTeXユーザの集いであった.そのなかで,TeXの限界というタイトルでいくつかのトピックが示された.私の知る限り,TeXチューリング完全なのだから,原理的に計算可能ならどうにかなるだろうと思った.実際,「え,list環境再実装すれb……」などの容易に実現できそうな項目もある.ただ,一つのトピックに目を奪われた.

画像のBase64埋め込みの非対応

確かに,TeXでバイナリを扱うのは面白そうな課題である.

やってみた.

といっても,Base64はまだ実現していないので,画像埋め込みをするお話*1

技術概要

某奥底にあるように,LuaTeXのような特殊な場合を除き,TeXエンジンでバイナリファイルを書くことは困難である.よって,TeXでバイナリで与えられる画像データを直接扱うのは不可能である.すなわち,テキストで書き出して,そのバイナリへの解釈をTeXの外部に任せることになる.さて,どうしようかと悩んだところで,ふと思いついた.EPSに出力して\includegraphicsで読めばいいじゃないか,と.EPSはテキスト形式なのでTeXから出力できる.また,ラスタ画像を埋め込むための命令もある.

作成した機能は,LaTeXのパッケージとして動作するようにした.そのパッケージ,binaryimage.styはここに置いておいた.このパッケージは,次のようにして利用する.

\documentclass{article}
\usepackage[dvipdfmx]{graphicx}
\usepackage{binaryimage}
\begin{document}
  ...
  \includebinary[size=wxh,option={width=6cm, clip}]<<バイナリの16進数表現>>\endbinary
  ...
\end{document}

画像を入れるために,includebinaryという命令を用意している.この命令は,オプションにsize(必須)とoptionをとる.sizeはwxhの形で幅と高さのピクセル数を与える.例えば,幅400px,高さ300pxの画像ならばsize=400x300とする.optionはこの命令によって自動的に挿入される\includegraphicsへ与えるオプションである*2.オプションの後には画像のバイナリを16進数表現したものを入れる.このバイナリは,JPEGなどの画像形式をそのまま入れるのではなく,「左上から右へ,右端まで行ったら次の行の左端へ」という順で対応するピクセルのRGBをそれぞれ1Byteで表現したものである.こんなPythonコードで変換できる*3

# -*- coding:utf-8-unix -*-
from PIL import Image
import sys


def main():
    img = Image.open(sys.argv[1])
    for j in range(img.size[1]):
        for i in range(img.size[0]):
            print("{:02X}{:02X}{:02X}".format(*img.getpixel((i,j))), end="")
        print()
    

if __name__ == "__main__":
    main()

実行すると,なぜか-binaryimage<0からの通し番号>.epsというファイルが生成され,\includegraphicsされる.epsファイルの中身は次のようになっている.

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 w h
/DeviceRGB setcolorspace
1 1 scale
<<
/ImageType 1
/Width w
/Height h
/BitsPerComponent 8
/Decode [0 1 0 1 0 1]
/ImageMatrix [w 0 0 h neg 0 h]
/DataSource currentfile /ASCIIHexDecode filter
>>
image

バイナリの16進数表現

ここで,w,h,および「バイナリの16進数表現」には\includebinaryで指定した値が入る.

これらの処理の結果,画像がtexファイルに埋め込まれたように見える.サンプルはここ(ソース結果).

実装小話(急カーブ注意×1)

ここで,binaryimage.styを実装するにあたり利用したテクニックを紹介する.

%の出力

TeXにおいて,%はコメントの開始文字なので,単純に出力できない.さらに,今回の場合は,\%も利用することはできない.\write(ファイル出力)のタイミングで\%が処理できないからである.そこで,binaryimage.styでは\lccodeを使ってコメント開始の役割を持たない%を作り出すことにした.

次のコードを見てみよう.

\lccode`+=`\%%
 \lowercase{\def~{+}}%

\lccodeはある文字に対して,その小文字の文字コードを指定するプリミティブである.上記のコードの1行目では,「+」の小文字に「%」を指定している.そして,\lowercaseを用いて「\def~{+}」の部分を小文字化する.これにより「\def~{%}」となるのだが,このときのカテゴリーコード(文字の役割)は変換前のものが使用されるため,「%」はカテゴリーコード12,すなわち一般の文字として扱われる.後は,%を出力したいところに~を書けば,~が展開されて%が出力される.

長いバイナリ入力への対応

\binaryimageでは,その用途の関係上,長大な入力を必要とする.しかし,TeXのマクロの引数として与えられる文字列はそんなに大きくない.そこで,今回は\edefを使って長大な入力を受け付けるようにした.

\binaryimageは,最初,オプション部分しか読まないようになっている.そして,\binaryimageを展開すると,末尾は次のようになる.

\edef\@bin@data{\ifnum`}=0\fi
       image^^J^^J

\@bin@dataの定義になっているように見えるが,閉じ波括弧が「\ifnum`}=0\fi」となっている.この部分が展開され,空になるので,上記のコードは

\edef\@bin@data{image^^J^^J

という閉じ波括弧がない未完成の\edefになる.いわゆる\ifnumトリックである.ここから,<<バイナリの16進数表現>>が読み込みおよび展開されていき,\endbinaryに達する.\endbinaryを展開すると,その先頭は次のようになっている.

\ifnum`{=0\fi}% end of \edef

これは,先の\ifnumトリックの対をなすコードで,閉じ波括弧単体となる.結果,\@bin@dataの定義が完成する.

この後,\endbinaryによってepsファイルの出力と\includegraphicsが行われ,画像が読み込まれる.

副次効果

binaryimage.styを通じて,Ghostscriptの闇などに触れてしまった.ここでは,その一部を紹介しよう.

Ghostscript9.10と9.15におけるヘッダ解釈の差異

現在,binaryimage.styが吐き出すepsファイルの1行目は「%!PS-Adobe-3.0 EPSF-3.0」となっている.実は,つい最近まで「%!PS-Adobe-3.0」という誤った出力をしていた.後者の出力の場合,Ghostscriptのバージョンが9.10なら動作するが9.15だと読み込めないという謎の事象に見舞われた.本記事の投稿が遅れた最大の理由がこれである.

epstopdfのMSYS対応

graphicxのドライバをpdftexにしたところ,指定した覚えのないディレクトリがエラーメッセージに出るという不可解な現象に遭遇した.中身を開いて調べたところ,epstopdfがMSYSに対応していないことが原因だと発覚した.仕方がないので自分で直し,フォーラムに投げた.現在は,Akira Kakutoさん,KUROKI Yusukeさん,およびKarl Berryさん*4のご尽力により,改良されたMSYS対応コードが本家に取り込まれたはずである*5

関連研究

ZRさんによって,このような記事がすでに発表されている.これはpdfTeXの機能を用いて,直接pdfファイル中に画像を埋め込む手法になっている.Ghostscriptに悩まされない分,綺麗な手法といえるが,pdfTeX依存になるため,TeXエンジンを複数使い分ける場合などに支障が出る.

今後の課題

Base64デコーダの実装が今後の課題にある.今回の使用目的に対してならいくつか実装アプローチがある.完全展開可能なデコーダTeX上に実装するか,PostScript側にデコーダを実装するかである.どちらもそれなりに苦行なので,そもそもbinaryimage.styに需要があるなら実施してもいいかもしれない*6

*1:某奥底に先を越されたのはGSの謎仕様のせい.

*2:命名規則が酷いのは仕様

*3:これが初Pythonであることを告白しておく

*4:著名人しかいない怖さ

*5:なお,全部rungsでもいいのではないかという提案を投げるのは私の仕事になっているが未実施である.仕事が早いのも見習いたい点である

*6:少なくとも,作者である私は使用する予定はない