ゆの in PostScript その2

せっかくの PostScript なんだから、やっぱり画像で表示した方がいいかな、と思ったので、PostScript viewer などで見られるようにしてみた。あと、文字だけでは寂しいので、顔の絵もつけて華やかにしたり。

%!PS
gsave
    4 dict begin
        /GothicBBB-Medium-EUC-H findfont 16 scalefont setfont % 文字コードが EUC-JP の場合
        /X{
            true
            {
                (365)show
                currentfile 80 string readline pop
                1 1 index length 1 sub getinterval show
            }
            {(ひだまり)show}
            count{exch}repeat ifelse
        } def
        /{
            (スケッチ)show
        }def
        /_{
            cvx exec
        }def

        gsave
            1 dict begin
                /showpage {} def
                0 -20 translate % 位置を調節して
                0.125 0.125 scale % サイズを調節して
                (tiger.ps)run % お好きな PostScript 画像をお貼りください
            end
        grestore
        80 30 moveto

        X / _ / X < 来週も見てくださいね!

        showpage
    end
grestore

解説してみる
PostScript の場合に問題になるのは < です。PostScript では、< は16進数にエンコードされたバイト列の始まりを意味します。(バイト列の終わりは > です。例えば <41 42 43> は ABC という文字列になります。)なので、< と > の間に 0-9、a-f、A-F、空白以外の文字が現れるとエラーになってしまいます。
それを避けるために、currentfile という命令を使います。currentfile はインタープリタがその時実行しているファイル(この場合ソースファイル自身)のオブジェクトをスタックに置くという命令です。このファイルはインタープリタが読んだ位置まで読み終わっているので、例えば readline でファイルから 1 行読み込むと、インタープリタが読んだ次の文字から改行までが文字列として返されます。currentfile と readline を使えば < 以降行末までを文字列として扱うことができます。
ということで、X を以下のように定義すれば、"ひだまり(略) 来週も(略)" と表示されることになります。

/X {
    (ひだまり(略)) print % タイトルを標準出力に表示
    currentfile 80 string readline pop % "/ _ / X < 来週も(略)" がスタックに置かれる。
    (<) search pop pop pop % 文字列を "<" で分解して "<" 以前を捨てる。
    print % 文字列を表示
} def
X / _ / X < 来週も(略)

ただ、このままだと / や _ を捨ててしまっているので、もったいないです。そこで / や _ を有効活用してみたところ、こうなりました。

/X{
    true
    {
        (365)show
        currentfile 80 string readline pop
        1 1 index length 1 sub getinterval show
    }
    {(ひだまり)show}
    count{exch}repeat ifelse
} def
/{
    (スケッチ)show
}def
/_{
    cvx exec
}def
X / _ / X < 来週も(略)

順に動作を見ていくと、

  1. 最初の X : まず true と {(365) ...} と {(ひだまり)show} が スタックに置かれる。count でスタックに置かれているオブジェクトの数を数え、3 がスタックに置かれる。{exch} をスタックに置いた後、repeat でスタックから 3 と {exch} を取り出し exch(スタックの上 2 個の入れ替え)を 3 回実行する。その結果スタックは true、{(ひだまり)show}、{(365) ...} の順になる。ifelse で (ひだまり)show が実行され、"ひだまり" と表示される。
  2. 最初の / : PostScript では / が頭についたトークンは実行されずにスタックに置かれ、頭に / が付かないトークンは実行される。/ は長さ 0 の文字列に対応する名前オブジェクト(以下 / と書く)を、実行せずにスタックに置くことになる。
  3. _ : cvx でスタックトップのオブジェクトを実行可能にして exec で実行する。スタックには / があり、/ は {(スケッチ)show} と定義されているので、(スケッチ)show が実行され、"スケッチ" が表示される。
  4. 2番目の / : 1 回目と同様に / がスタックに置かれる。
  5. 2番目の X : true と {(365) ...} と {(ひだまり)show} がスタックに置かれ、スタックに /、true、{(365) ...}、{(ひだまり)show} の 4 個のオブジェクトがある状態になる。count{exch}repeat で 4 回スタックの上 2 個が入れ替えられ、元の /、true、{(365) ...}、{(ひだまり)show} に戻る。ifelse で (365)... が実行され、"365" と " 来週も(略)" が表示される。
    1. つまり、X の挙動をスタックにあるオブジェクトの数に従って変えています。まあそれだけなら count 0 eq{(ひだまり)show}{(365)...}ifelse でいいのですが、条件分岐が特別な構文ではなくて、スタックと命令になっていることを示したくて、やってみました。
  6. インタープリタは "来週も(略)" の改行まで既に読んでいるので、次の行から実行が続く。

となります。つい無駄に複雑化させようとするのは、悪い癖です。