HotMilk ――とある古本屋で働く青年はこの名前を嫌がっとったが―― では、ユーザー定義ボタンの実装は簡易のインタープリタを用いて実現している。
要は文字列を先頭から検索して、${...} の形式を見つければマクロ変数として扱う実に簡単明瞭な処理である。ちなみに余談だが、未定義のマクロ変数に関してはその変数の値を文字列で入力させ、以後の同じマクロ変数に対しては自動的にその値を入力したものとして扱う。この仕様を思いついた時に「勝った!」と思ったらしい(何にだ)
現段階では単なるプリプロセッサだが、最終的には「せめて MS-DOS のバッチ言語」ぐらいには仕上げたいと思っている。できれば PASCAL 的関数言語とは言わないまでも BASIC 程度の構造化は果たしたい……。まぁ HotMilk もファーストバージョンの間はプリプロセッサで上等だが。
そのような目論見(もくろみ)があるので、取り敢えずエディタに対してメインフォームのメンバで処理していたマクロ変数の置き換えを独立してクラス化した。今のところ改行文字とかを意識しないので、Memo の Text プロパティなんかでも正しく動作する。
この流れで HTML の新規作成時にテキストファイル内のマクロ変数をも予め書き換えられるようにした。これによって、HotMilk では新規ファイルの作成が楽になった気がするのだが、定かではない。
新規作成機能それ自体は完全とは言えない。このホームページの構成では各記事には連番が付けられているのだが、そう言うファイル名を自動的に生成するべきではないかと思っている……今はファイル名をつけない状態でスタートするのだが、どちらが良いだろうか?。
その辺りもテンプレートにマクロ変数を仕組むだけで重複しない連番ファイル名を生成させることはできるだろうが、テンプレート内であれば正しく動作するとは言え、おもむろに編集ボタンで指定されたらどうなるんだろう……とかね。
プリプロセッサ自体はエディタ部とは縁を切っているのでファイル名の設定は厄介だ。今もグローバルなスコープをもつ変数でエディタを参照している。申し訳程度にレコードに含めて可読性を保持しているだけだ……
プリプロセッサ部は内蔵してもいいかもしれないが、あまりそういう重装備な仕様は望んでいない。HTML ソースエディタへの着色機能とマクロ置き換えとは無縁のものであろう、と言う思想である。
継承して搭載するのは悪くはないかな……?
マクロの解釈に付いては着色処理の際に得た経験が役に立った。アルゴリズムはほぼ同一であり、強いて言えば多バイト文字を考慮した事ぐらいだ。
現状ではマクロに対するオプション指定の際の処理が手落ちである。スペースや改行コードでは区切りと見なせず1文字として扱うので、指定に失敗している事になる。優先的に処理せねばなるまい。
それからマクロのネストも書けないか? ファイルを開くコマンドを実行しパスを得たい時、引数となるマスクを先に選択させてそれを引数とする……と言った処理はいいかな。
ダイアログで選択したファイルからタイトルを抜き出して置き換えたりとか、細かな変数も必要になるだろう。
で、HTML を解析するついでにタイトルだとか本文のアトビリュート(BODY タグに付随するものとか)とかを確保しても良いかもしれない……が、そう言う処理って以外と難解だ。<HEAD>〜</HEAD> の間だけの情報とは言え、ここに登場しうるタグはけっこう多い……
そう言うイメージで、新たにパーサーを作成しても良いかも。どうせ標準の TParser はパスカル構文に特化しててそのままでは使えないからだ。後述。
要は重要なタグで括られた文字列を TStrings にでも放りこめば便利なわけ 'タグ=文字列' って感じに…… 'TTTLE=うひょうひょ' とか。後は IndexOfName とか Values とかで値を取り出せば良いわけだから、なにかと便利かもしれん。
TParser が使えないのには参った。TRichEdit のデフォルトのエンコーダ(特定のクラスではなく内蔵らしい)は TParser を使わないので気がつかなかったのだが、元々 TParser は Delphi IDE の着色用に設計されているらしいのだ。
詳しくは TParser.NextToken を参照すれば判るが、このメソッドは virtual で宣言されていないのでオーバーライドできないのだ。つまり、継承しても最終的にはパスカル構文の制限を受ける事になるのである。
HTML の中に、例えば「'99/12/31」という文字列があると、シングルクォートが改行コードまでに閉じられない事になって例外が発生する。パスカルはそうなのでそれは良いのだが、普通に文章をエンコードする場合には不要以外の何物でもない。
オーバーライドできないので上書きで実装できるか?というとそれも難しい。NextToken で使う変数や関数に Private フィールドに属するものが多いのだ。別のユニットから参照するには少々アクロパティックな方法を使わねばならない。
Private フィールドを参照する方法は、Delphi-ML のバックログぐらい探せば何かあるかもしれないが……確実なのは InsideWindows 誌の 98-12 号の「Delphiの神託」の記事中にある。ちなみに TRichEdit のエンコードの解説の絡みで説明されているわけで、問題点は同じってワケだ……1年ぐらい気がつかなかったのは情けない限りだが(笑)
個人的には、プライベートフィールドを参照するってのは良くないと思っている(^^;)
なので、HotMilk では TParser のクローンを作成してタグの解釈に特化している。こんな感じだ。
function TTagParser.NextToken: Char; var P : PChar; Tag: String; begin SkipBlanks; P := FSourcePtr; FTokenPtr := P; case P^ of '<': begin Tag := ''; repeat Inc( P ); Tag := Tag +P^; until (P^ = '>') or (P^ = tcEOF); Inc( P ); // これがないと '>' を含めてくれないの…… if AnsiSameText( Copy(Tag,1,2), 'BR' ) then Result := tcTagReturn else if AnsiSameText( Copy(Tag,1,3), '!--' ) then Result := tcTagComment else Result := tcTag; end; else Result := P^; FStringPtr := P; if Result <> tcEOF then Inc(P); end; FSourcePtr := P; FToken := Result; end; |
必要な手続き、変数類もまとめてカット&ペーストしてあるので TParser には依存していない。
ちなみに virtual してないので後々後悔する羽目になろう(笑) まぁ、SkipBlanks などを Protected フィールドに格上げしたので上書きで何とかなるかもしれない。変数類は必要になればプロパティでも作るか……
と言うかアレだ、NextToken からイベントハンドラを呼び出してアプリ側から定義できるようにしても良いんだな。うん。
今回は最新版のアップロードは無し。次回に譲る。な〜に、原稿は上がっているのさ(笑)
つーか長すぎたので分割したの……