マクロプロセッサ(の自作)のすすめ

「マクロプロセッサ自作のすすめ」、ではありません(宣伝 http://tatsu-zine.com/books/fpga

この記事は Independent Advent Calendar 2015 の記事です。

前提

杉浦 K. さんによる「m4 チュートリアル」や、拙作「M4入門」といった、テキストマクロプロセッサM4の解説は既にあるわけですが、この記事ではもう少しメタな話、マクロプロセッサの選び方とかそういう話をしたいと思います。

Cプリプロセッサ

少し古いタイプの、一応C言語も(普段は他の言語を使っていても)一通り使えるタイプのプログラマには一番馴染み深いのがCプリプロセッサかもしれません。Cプリプロセッサについては、松井潔さんによるmcppにより、概念の整理や良い実装とドキュメントがありますので、そちらをまずは参考にするのが良いでしょう。

一方でCプリプロセッサでは、入力を取り込むトークンの単位が、C言語の標準で「プリプロセッサトークン」として定められているものであるように、C言語の、字句(字句レベルの)構文(lexical syntax)とそれなりに緊密に結びついているものであって、汎用のテキストプロセッサではない、と考えるべきでしょう。

Lispマクロ

Lispのマクロは、リストであらわされた構文木という既に構造のあるものを、構造から構造へ変換するものですから、ここで扱いたいテキストマクロとは異なります。

アドホックスクリプティング

実際のところ一番多いのがこの、その時その時にテキスト処理したい内容に応じて、sedawkテキストエディタのマクロによるプログラミングで作業をこなす、というスタイルではないでしょうか。ここで、アスキーの『MS-DOSを256倍使うための本 vol.3』にあるsedによるroffや、sedでlispを作ってしまうような人には私から申し上げるようなことは何も無いでしょう。それはともかくとして、以前はawkの鬼門のひとつだったバイナリの操作も、C言語ではなくPerlや、もっと現代的なスクリプティング言語で扱えるようになりましたから、テキスト以外にも応用が効くなど柔軟性の高い解でもあります。

M4

しかし、もう少しテキスト処理に特化した解はないのか、というわけでM4です。が、詳しいところは最初に挙げたチュートリアル等を読んでみてください。

「汎用」と言われてはいますが、マクロ呼出しの構文が macro_name(arg1, arg2, ...) というようなスタイルに限定されていて*1、出力側はまだしも、入力側はC言語と同様な構文に限定されますので、あまり自由度がありません(なおCプリプロセッサと同様に、このような形になっていても、マクロとして定義されていない名前であればスルーします)。

またこのような構文のせいで、どこからマクロ名が始まっているのかが純粋に文字だけからはわからないため、正規表現で表現すると [A-Z_a-z][0-9A-Z_a-z]* というようなパターンにマッチする範囲をまとめてトークンとして読むという、純粋にテキストベースのマクロプロセッサではなく、Cプリプロセッサのようなトークンベースっぽい動作が含まれてしまっています(マクロのトークンベース・テキストベースといった話は前述のmcppのドキュメント等を参考にしてください)。

以上のような問題点はありますが、どのUnixにもまず存在し(多くのフリーなBSD系のシステムであれば、パッケージ等のインストールの必要なく /usr/bin/m4 があるでしょう)、実装もノウハウも枯れているM4を使う、というのは悪い選択ではない、と思います。

マクロプロセッサ自作

というわけで(やっと)マクロプロセッサ自作についてです。

前述のように、M4にはいまいちの点があるわけで、他にも自作のメリットとして「自分で作っているので、何か謎な時に簡単に内部に潜って追いかけることができる」といったことがあります。というわけで、自作についてのアドバイスをいくつか書きます。

まず最初に作るものとしては、前述の普段作っているようなアドホックスクリプトをベースとして、汎用っぽくなるように改造したもので良いでしょう。他に、もし最初から作るのであれば、awk使いならJon Bentleyさん(名著『珠玉のプログラミング』の著者です)がawkで書かれたm1というマクロプロセッサも良いでしょう。ネット上では http://www.drdobbs.com/open-source/m1-a-mini-macro-processor/200001791 に解説があります。書籍ではオライリー『sed & awkプログラミング 改訂版』の13章の最後で紹介されている良作です。

本格的自作

基本的な部分はM4と共通な、あるいはM4に対抗できるようなマクロプロセッサの自作について考えます。GNU m4 は長らく 1.6 が開発中ですし、うまくすれば新デファクトスタンダードを取れる可能性もあるかも?

M4では、マクロ定義についても「新しいマクロがその場で定義される」という副作用を持つマクロ(通常の(副作用を持たない)マクロは、置き換えが起きるだけ)、という扱いになっていて、クウォート(エスケープ)やコメント以外「全てはマクロ」というのが徹底しています。このようなM4のようなスタイルのマクロプロセッサの作り方としては、『ソフトウェア作法』の8章が参考になるでしょう(名前的にM4の先祖であるM3というマクロプロセッサが、同書の影響で作られたとも、同書がその影響を受けているとも言われています(資料によりまちまちでどちらなのかよくわからない))。ただし前述のように、M4の微妙にトークンベースの挙動などについても同様ですので、M4を越えるつもりならばもう一歩進む必要があります。なお、深く比較してみると同書が解説している内容とM4とは、マクロ名からその展開の骨組み(ボディ)を得ているタイミングが違うなど、微妙に違う点がけっこうあります。

というわけで筆者が自作したのがMacr055です。略が「M5」になるのはちょっと畏多いのと、名前がカブりまくるだろうと考えて、「M55」になるようにしました(あと、macrossという綴りに数字を当てた形にもしている)。awk千行野郎としてはawkで書かねばだろう、と思って私はawkで書きましたがかなり壁も感じるので、もっと高機能な近年のスクリプト言語を使ったほうが良いかもしれません。

実は、M4よりもさらに歴史を遡る、Christopher StracheyさんのGPMことGeneral Purpose Macrogeneratorは、その名の通り汎用さが意識されていて、マクロ呼出しではマクロ名にプレフィックスを付ける、という、完全にテキストベースで動作できる設計になっています。GPMについては、和田先生による一連のブログエントリ(http://parametron.blogspot.jp/search/label/Christopher StracheyのGPM)や、原論文 http://dx.doi.org/10.1093/comjnl/8.3.225 が参考になるのですが、和田先生のブログはともかく(?)原論文は半世紀前に書かれたものですからコの業界の時間感覚では完全に古文書であり「解読」は少々骨かもしれません……が、とはいえ論文中の記述によれば(メモリが極小だった当時のテクニックがふんだんに使われたのだろうとはいえ)最初のバージョンは(機械語で)たった250命令(250 orders、機械語命令をorderと呼ぶのは古代語)だったということでそう大きなプログラムでもなく、内容も章ごとにほぼ完全に分かれていますから全てを解読する必要もありません。

*1:なお、UNIX Programmer's Supplementary Documentsに収録のドキュメントThe M4 Macro Processorの概要で、このような記法を「functional notation」と呼んでいるのはともかくとして、そのような記法を使う言語という意味で「functional languages」という、@esumii先生に怒られそうな表現があったりします。著者はC言語K&Rと同じお二人ですが、このころはまだプログラミング言語の理論等には詳しくなかったのでしょう……(たぶん)。