SourceForgeのFeature Requestでみつけたので、ヒアドキュメントについて考えてみた。ヒアドキュメント欲しかったのだ。
Perlのヒアドキュメントではこんなふうに書ける。ヒアドキュメントを重ねられることに注意。
print <<EOF1, <<EOF2; abc EOF1 def EOF2
このコードは次のコードと同じ意味になる。
print " abc\n", " def\n";
Perlのヒアドキュメントに対して、Checkenの#<<TAGは重ねることができない。#<<TAGのある場所(次の行ではなくて)から文字列が始まる。つまり、文字列を閉じる記号にダブルクオート以外を選べるリーダ拡張にすぎないので、ヒアドキュメントというよりは、Perlのq/STRING/や、Pythonの...
のほうが機能的に近い。
Perl式のヒアドキュメントを実現しようとすると、#<<TAGが現れたとき、まず行の残りの部分をどこかにとっておいた上で、次の行から"TAG"単独の行までを読んで返す必要がある。次にreadを実行したときは、保存しておいた行の残り部分から読み込まないといけない。これはポートに無制限に文字をプッシュバックできれば簡単だけど、そういう機能はないので、実現がちょっと難しい。
Checkenの#<<TAGや、Perlのq/STRING/、Pythonの...
などは実現が簡単。
でもどちらかといえば欲しいのはPerl式のヒアドキュメントなのだ。ヒアドキュメントでなければ、行という概念にとらわれず、q/STRING/を実装したほうがよい気がする。ということをChicken式のを実装して思いましたよ。オイオイ。
改めてPerl式のヒアドキュメント実装してみました。
無制限のプッシュバックを許す代わりに、ポートをスタックできるようにしました。ポートにスタックを設けて、そこに別のポートをプッシュできるようにします。スタックが空でなければ、読み出しはスタックトップのポートから行われます。スタックトップのポートがEOFを返すと、そのポートはスタックから取り除かれ、次のスタックトップのポートから改めて読み出しが行われます。スタックがない場合の動作は現状と同じです。
上記のメカニズムを利用して、行の残りの部分を文字列として保存しておき、ヒアドキュメント本体を読み込んだ後、文字列ポートを元のポートにプッシュします。これによって、行の残りの部分が再度ポートから得られるようにしています。
上のアイデアはSFIOからもらっているのですが、実はあまり気に入っていません。もっと一般的な方法がありそうな。実験的ですがパッチ。標準でサポートされていないリーダ拡張を実際のプログラムで使うと後で苦労することになるので、その点注意してください。
なお構文ですが、#<<TAGと#<<`TAGをサポートします。後者は#`"..."に対応します。次のように使えます。
gosh> #<<EOF abc EOF ;; => "abc\n" gosh> #<<`EOF ,(gauche-version) EOF ;; => "0.8.7_pre1\n" gosh> (list #<<EOF #<<`EOF) abc EOF ,(gauche-version) EOF ;; => ("abc\n" "0.8.7_pre1\n")
(list #<<EOF) abc EOF ;; => "abc" (改行なし) (list #<<EOF) abc EOF ;; => "abc\n" (改行あり)