Win32 コンソールアプリケーション 汎用マクロ展開フィルタ HM v1.14 html の記述などに使用できる、汎用テキスト置換タイプのマクロシステムです。い わゆるプリプロセッサで、マクロの展開や他のファイルの include が可能です。ユー ザーマクロを自由に定義することができ、また更新時間などを自動で埋め込むことも できます。 html を手書きで作成する場合や、C言語のプリプロセッサとして使えます。Cコンパ イラについている make コマンドと併用すると更に便利です。Makefile に記述してお けば、更新したページだけの変換を一気に行うことができます。 1.14: $(include) の引数が正しく展開されていなかったバグを修正した。 1.13: 画像サイズを正しく取得できなかった jpeg があったので対応した。png にも 対応した。 ●実行方法 hm [-d] [-c] [-o出力ファイル名] 変換ファイル名 [引数..] 出力ファイル名を省略すると、結果の出力を標準出力に行います。引数を指定した 場合は、ファイル内で $1〜 で参照することができます。 switch: -c C言語プリプロセッサ命令 #line の出力を行います。C言語のプリプ ロセッサとして使う場合はこのオプションをつけてください。コン パイラエラーメッセージの行数表示が正確になります。 -d デバッグモードで起動します。 -o出力ファイル名 出力ファイル名を指定します。 ●置換マクロのフォーマット マクロはすべて $ で始まる文字列です。引数がない場合、かつ前後の文字列との 区切りが不要な場合は $マクロ名 という記述をします。マクロ名を明確に区別する必要がある場合は $(マクロ名) のように括弧で囲ってください。また、マクロ展開に引数を与える場合は $(マクロ名,引数1,引数2,...) のように、カンマで区切って与えます。 引数やマクロ名は、再変換の対象となります。つまり、マクロの中にもマクロを記述 することができます。 ●システムマクロ 最初から組み込まれているマクロです。以下の説明では、引数がないマクロの場合 はすべて括弧の記述を省略しています。 ・特殊マクロ $; コメント 行末までをコメントとみなします。 $$ 文字 $ になります。 $, 文字 , になります。 ・引数展開マクロ $0,$1,〜$99 カレント引数に置き換わります。カレント引数は、コマンド起動直後はプロ グラムのコマンドラインが、include ファイル内では $(include) の引数が、 マクロ展開内ではマクロに与えた引数になります。$0 にはマクロ名やファイ ル名が入ります。引数を与えなかった場合や未定義の引数には、空文字列が 入ります。引数は最大 99個です。 ・定義文マクロ $(include,ファイル名[,引数..]) 指定ファイルの内容を挿入します。必要なら引数を与えることができます。引 数の値は、ファイル内で $1〜 によって参照できます。 $(nop,コメント) 何もしません。コメントの部分を無視します。 $(define,定義名前,定義内容) マクロを定義します。定義したマクロは、システムマクロと同様に使うことが できます。定義内容の部分は、実行時に展開が行われます。実行時展開の引数 を持つマクロはこのコマンドだけです。 $(set,定義名前,定義内容) マクロを定義します。定義したマクロは、システムマクロと同様に使うことが できます。定義内容の部分は、定義時に展開が行なわれます。$(define) と違 い、すでに定義してあるマクロの上書きが可能です。set の定義内容で引数を とりたい場合は、$$1 のように $ が変換されないようエスケープする必要が あります。 ・制御文マクロ $(if,条件式,展開文字列1,展開文字列2) 条件式が成り立つ場合は展開文字列1に、そうでない場合は展開文字列2に置き 換わります。条件式は、数値とみなせる場合は 0 でない値、文字列の場合は 空文字列でない場合に条件成立とみなします。 $(select,式,展開文字列1[,展開文字列2..]) 式個目の展開文字列に置換します。式が 0の場合は何も展開しません。 $(echo,式) 式の値によって以後のテキストの出力を制御します。 式の値 0 この命令は何もしません -1 以後の出力を行いません(参照カウンタを -1する) 1 出力を行います(参照カウンタを +1する) 出力が OFF になっていても、マクロの実行自体は行なわれています。この呼 び出しはネスト可能で、出力制御を OFF にした回数だけ再び ON にしないと 出力が行なわれません。巨大もしくは複雑な文章の出力を分岐させる場合は、 $(if) より $(echo) で分岐させるほうが向いています。 <例> 出力する $(echo,-1) 出力しない $(echo,-1) 出力しない $(echo,1) 出力しない $(echo,1) 出力する ・ファイル情報マクロ $(@FILESTAT,ファイル,フォーマット) 指定ファイルの情報に置き換わります。フォーマット内には、次のフォーマッ ト文字列を含めることができます。 %y 年2桁 %Y 年4桁 %M 月 %d 日 %h 時 %m 分 %s 秒 %b ファイルサイズ byte 単位 %k ファイルサイズ Kbyte 単位 %e ファイルサイズ Mbyte 単位 %f コマンドに与えられたファイル名そのまま %n ファイル名の名前部分のみ %p コマンドに与えられたファイル名のパス部分のみ $(@FILENAME[,フォーマット]) 自分自身のファイル名に置き換わります。フォーマットを指定した場合は次の フォーマット文字列が使用できます。(省略時は $(@FILENAME,%f) 相当) %f コマンドに与えられたファイル名そのまま %n ファイル名の名前部分のみ %p コマンドに与えられたファイル名のパス部分のみ $(@IMGSIZE,ファイル,フォーマット) 指定した画像ファイルの情報に展開します。使用できるフォーマット文字列は 以下の通り。現在 gif/jpeg/png に対応しています。 %w 横サイズ %h 縦サイズ %f 与えたファイル名そのもの(パス込み) %n ファイル名のみ %p ファイル名のパス部分 %b ファイルサイズ byte 単位 %k ファイルサイズ Kbyte 単位 %e ファイルサイズ Mbyte 単位 <例> $(@IMGSIZE,hyon.gif,width=%w height=%h) ・式演算マクロ $(@expr,式文字列[,フォーマット]) 式文字列を数式とみなして計算し、その結果に置換します。フォーマットを 指定しない場合は結果は 10進数で返します。数値範囲は整数 32bit で、演 算はすべて整数演算です。数値定数は 10進数、16進数 (html のカラー指定 と同じように先頭に # をつける) が使えます。使用できる演算子は次の通り。 (演算優先順位の高い順) () 括弧 !,~ 論理反転、ビット反転 *,/,% 乗算、除算、剰余 +,- 加算、減算 <<,>> シフト演算 >,<,==,>=,<=,!= 関係式(成り立てば1,そうでなければ0) &,|,^ ビットAND,OR,XOR &&,|| 論理AND,OR フォーマットを指定した場合は、以下のフォーマット文字列を含めることが できます。 %d 計算結果の10進数表現 %x 計算結果の16進数表現 <例> $(if,$(@expr,$A==$B),等しい,等しくない) color=$(@expr,($RED<<16)+($GREEN<<8)+$BLUE,#%x) $(@expr,#ffff+$S*8-$B) ・文字列比較マクロ $(@eq,式1,式2) 文字列比較で等しければ1を、そうでなければ0になります。数値比較を行う 場合は $(@expr) を使用してください。 $(@ne,式1,式2) 文字列比較で等しくなければ1を、そうでなければ0になります。数値比較を 行う場合は $(@expr) を使用してください。 ●ユーザー定義マクロ $(define) や $(set) で定義したマクロを、以下のフォーマットで使用することが できます。もちろん引数がない場合は括弧を省略できます。 $(定義名[,引数..]) マクロ展開の内部では、引数をそれぞれ $1 〜 で参照することができます。$0 はマ クロの定義名になります。 ●ブロッククオート マクロの引数はカンマ「,」で区切ります。また、各引数の先頭の空白と改行は、無 視されてしまいます。複数行にわたるような引数をそのまま渡したい場合、もしくはカ ンマなどの文字を通常の文字として引数に含めたい場合は、ブロッククオート「{〜}」 を使います。 引数全体をブロッククオートで囲むと、その間は改行が空白も保存され、またカンマ や括弧も特別な意味を持ちません。 <例> $(define,BLOCK,{ これがクオートされているところ }) $(if,$V,{ これがクオートされているところ },{ これもクオートされているところ }) ●このツールを活用するための方法 ・何度も出てくる定数はマクロにする 例えば自分のメールアドレスのように、何度も記述する必要がある文字列は、定数 として最初にマクロ定義をしてしまいます。 $(define,MAIL,oga@art.udn.ne.jp) これで、以後メールアドレスを記述する場所には $MAIL と書くだけで済みます。 同様に、トップページのアドレスも $(define,TOPPAGE,http://www.hyon.udn.ne.jp/) のように定義しておくと便利かも知れません。もし将来アドレスを変更する必要が生じ た場合でも、この $(define) 部分の変更だけで済むようになります。 ・共通事項は一つのファイルにまとめておいて include する 例えば上で説明したメールアドレスなどの定数は、html ファイルすべての共通事項 です。このようなマクロ定義は一つのファイルにまとめてしまい、あとはそれぞれの html ファイルの先頭で include してしまえば便利です。 $(include,読み込むファイル) と書くだけで、ファイルの内容がその場所に取り込まれます。 ・マクロの再定義 引数を伴う長いマクロでも、再定義してしまえば括弧なしで簡単に記述できるように なります。例えば、自分自身のファイルのサイズは $(@FILESTAT,$@FILENAME,%b) で取り出すことができます。しかし、これをそのまま利用するには長すぎるので、 $(define) で次のように定義してしまいます。 $(define,FSIZE,$(@FILESTAT,$@FILENAME,%b)) これで、$FSIZE と書くだけで自分自身のファイルサイズをいつでも参照できるよう になります。 ・条件分岐 例えば似たようなページを複数作る場合を考えます。日本語と英語のページを作る 場合でもいいですし、本家とミラーに置くページ、またはブラウザやフレーム機能に よって同じ内容でもファイルを分けたい場合も考えられます。 このような場合は、違うところだけを $(if) で分岐させると、一つのファイルに書 くことができます。 -- hyon.src $(if,$1,ひょーんプロジェクト,HYonProject) 例えばこのような内容の hyon.src というファイルを作ったとします。これを、 hm hyon.src 1 のように、引数として 1 をつけて実行すると、$(if〜 の部分は「ひょーんプロジェ クト」という出力になります。後ろの数値を 0 にすると「HYonProject」になります。 つまり、コマンドに与える引数の数値一つで、同じファイルから英語ページを出力した り、日本語ページを出力したりと切り替えができます。 ●その他の技法 ・無駄な改行を無くすには マクロ定義ファイルを $(include) すると、include した部分に改行だけの行が多 数挿入されてしまうことがあります。これは、$(define) などの後ろに書いた改行も、 文字として出力の対象になっているからです。 html の場合はそれほど問題ありませんが、気になる場合は include するファイルの 先頭に $(echo,-1) を、最後に $(echo,1) を書き込んでおいてください。これらは ファイルへの出力を OFF/ON する命令で、単純なマクロ定義の間は出力を OFF にして おけば余計な改行が出力されません。 ・ファイルのディレクトリの区切りは / で書く DOS や Windows では、ファイルのパスのディレクトリの区切りとして「\」を使い ます。しかし、URL ではパスの区切り記号として「/」を使います。hm はマクロ参照 時のファイルは「/」で区切ることができるようになっているので、URL との互換性を 考えて、「/」を使用するようにしてください。 <例> $(@FILESTAT,data/dosfile.dat,%f %b) ●わかりにくいところの解説 ・$(define) と $(set) の違い $(define) と $(set) は、どちらもユーザーマクロの定義を行います。基本的には 同じような働きをしますが、この両者は定義内容に含まれるマクロが参照されるタイ ミングが異なっています。 例えば次のように、$VDEF と $VSET の両方に $A というマクロを定義します。定義 が行なわれる段階では、$A には「その1」が入っています。$VDEF と $VSET のマクロ を記述する段階では、$A には「その2」が入っています。 $(set,A,その1) $(define,VDEF,$A) $(set,VSET,$A) $(set,A,その2) defineの結果「$VDEF」 setの結果「$VSET」 結果は、次のようになります。 defineの結果「その2」 setの結果「その1」 つまり、$(define) では $A という文字列がそのまま記憶されるのに対し、$(set) で は、定義した時点で「その1」という文字列に展開されてから記憶されているわけです。 この違いは、両者の命令の意味と用途が異なっているからです。$(define) は普通の マクロ定義で、単純に定数を保存します。おそらく通常の使用では $(define) だけで 何の問題もないはずです。対する $(set) は、上記の $A のようにマクロを何度も再定 義し、変数としてアクセスできるように用意されたものです。よって、$(set) ではシ ンボルの再定義がエラーになりませんし、定義したその時点での値を格納します。 $(set) の活用例としては、例えば include ファイル内で、引数をマクロに定義する 場合があります。 -- sp1.inc $(set,ARG1,引数$1) $ARG1 -- main.src $(include,sp1.inc,これ) 上のような 2つのファイルを用意し、main.src を hm にかけると、$ARG1 には 「引数これ」という文字列が定義されます。 もしこれが $(define) なら、マクロ内に書いた $1 は、マクロに与えられた引数の 展開とみなされるので、include ファイルに与えられた引数「これ」をマクロ定義内部 で参照することができません。$(set) を使えば、$1〜 は定義した時点のものがそのま ま展開されるので、このような include ファイルの引数なども定義部分に取り入れる ことができます。その代わり、$(set) で定義したマクロに与えられた引数を受け取る には $ が変換されないように $$1〜 と記述する必要があります。 ●マクロ記述の技 マクロの展開は、きわめて自由度が高くなっています。例えば、マクロ名自身も引数 とまったく同じ扱いであり、マクロ変換の対象となります。よって、 $(set,ABC,適当な文字列) $(set,X,BC) $(A$X) という記述を行うと、3行目の $X は 2行目で定義されているように BC に展開され、 $(ABC) となります。$ABC は 1行目で定義されているため、最終的に「適当な文字列」 が展開されます。 これをうまく利用すれば、引数を配列のようなインデックスとして参照したり、呼び 出すマクロ名をマクロで与えることも可能になります。 ●使用例 ◎マクロ展開の例 ---------------------------------------------------------------------------- $(define,NAME,小笠原博之) $(define,MAIL,ogal@art.udn.ne.jp) $NAME $(MAIL) ---------------------------------------------------------------------------- 出力: 小笠原博之 oga@art.udn.ne.jp ◎更新時間生成マクロ ---------------------------------------------------------------------------- $(define,MTIME,$(@FILESTAT,$@FILENAME,%y-%M-%d %h:%m)) 最終更新時間 $MTIME ---------------------------------------------------------------------------- 出力: 最終更新時間 98-01-16 17:57 ◎ color指定の RGB 値を、それぞれ10進数で指定できるマクロ ---------------------------------------------------------------------------- $(define,RGB,$(@expr,($1<<16)+($2<<8)+$3,#%x)) ---------------------------------------------------------------------------- 出力: ◎ Makefile への登録例 (元ファイルを *.src とする) ---------------------------------------------------------------------------- ## VisualC++ nmake 用 all: index.html .src.html: hm -o$@ $< .SUFFIXES: .html .src ---------------------------------------------------------------------------- ●Version 1998/01/14 v1.00 1998/12/28 v1.10 1999/01/09 v1.11 2000/10/25 v1.13 jpeg画像サイズ取得部分改良、png対応。動作テストしていただいたしいねさんに感 謝します。 2001/06/10 v1.14 $(include) の引数が正しく展開されていなかったバグを修正した。(報告 たかみ (takami) 氏) -- 小笠原博之 oga@art.udn.ne.jp http://www.vector.co.jp/authors/VA004474/ http://www.hyon.udn.ne.jp/