この章では,m4
マクロ・プリプロセッサについて説明します。
この
m4
マクロ・プリプロセッサは,ソース・ファイルの最初の部分に
m4
マクロ定義を設定することにより,ユーザのマクロ定義を可能にするフロント・エンド・フィルタです。m4
プリプロセッサは,プログラム・ソース・ファイルにも,文書ソース・ファイルにも使用することができます。
この章では,マクロについての次の情報について説明します。
マクロを使用すると,大量の内容を 1 語または 2 語で代用することができるため,プログラミングや入力作業が容易になります。 ソース・ファイルのマクロ呼び出しには,次のフォーマットを使用します。
name
[ (
arg1[ ,
arg2] ) ]
たとえば,何度か同じメッセージを表示する C プログラムがある場合には,次のように,printf
文をコーディングすることができます。
printf("\nThese %d files are in %s:\n",cnt,dir);
プログラムの改良に伴なって,メッセージの内容を変更することにした場合,メッセージの各インスタンスを編集する必要があります。 次のようにマクロを定義すると,大量の作業を省略することができます。
define(filmsg,`printf("\nThese %d files are in %s:\n",$1,$2)')
このメッセージを出力するすべての場所で,マクロを次のように使用します。
filmsg(cnt,dir);
このように指定されていれば,メッセージを 1 箇所で編集するだけですべての表示が変更されます。
マクロ定義は,記号名 (トークン) と,それに置き代わる文字列によって構成されます。
トークンは,英字または下線で始まる英数字 (英文字,数字,および下線) の文字列で,英数字以外の文字 (句読点またはホワイト・スペース) で区切られます。
たとえば,N12
と
N
はどちらもトークンですが,A+B
はトークンではありません。m4
を使用してファイルを処理すると,マクロが認識されるたびにマクロがその定義と置換されます。
シンボリック・ネームをテキストと置換するだけではなく,m4
は,次のような動作を実行することもできます。
算術計算
ファイル操作
条件付きマクロ展開
文字列および部分文字列関数
システム・コマンドの実行
m4
プログラムは,ファイルの各トークンを読み取り,トークンがマクロ名であるかどうか調べます。
他のトークンに埋め込まれたマクロ名は認識されません。
たとえば,m4
は,N12
にトークン
N
が含まれているとは解釈しません。
トークンがマクロ名の場合,m4
はそれを定義されたテキストに置換し,結果として生じた文字列を入力に戻し,再走査を実行します。
このように,マクロ展開は再帰的です。 マクロ定義には,どんなに深くネストした他のマクロでもインクルードすることができます。 引数を付けてマクロを呼び出すこともできます。 この場合,引数はまとめられ,定義されたテキストを再走査する前に,定義テキストの適切な位置に代入されます。
m4
プリプロセッサは標準の UNIX フィルタです。
標準入力,またはリクエストされた入力ファイルからの入力を受け入れ,その出力を標準出力に書き込みます。
次に,正しい
m4
の使用例を示します。
% grep -v '#include' file1 file2 | m4 > outfile % m4 file1 file2 | cc
m4
プログラムは,各引数を順番に処理します。
引数が全くないか,または引数がマイナス記号 ( -
) である場合,m4
は入力ファイルとして標準入力から読み取ります。
5.2 マクロの定義
m4
に実装された約 20 個の組み込みマクロの 1 つである,define
コマンドでマクロ定義を作成します。
たとえば次のように定義します。
define(N,100)
開き (左) カッコとワード
define
の間にスペースを入れてはなりません。
このマクロ定義によって,トークン
N
は処理されたファイル内のどこに現れても
100
に置換されます。
定義テキストは,どんなテキストでもかまいませんが,テキストにカッコが含まれる場合を除きます。
この場合は,対応していないカッコを引用符で囲って保護しない限り,開き (左) カッコの数と閉じ (右) カッコの数が一致していなければなりません。
引用についての詳細は,5.2.1 項を参照してください。
組み込みマクロとユーザ定義マクロは,組み込みマクロの一部が処理の状態を変更することを除いて,同じ方法で動作します。 組み込みマクロの一覧については,5.3 節を参照してください。
マクロを他のマクロとして定義することができます。 たとえば,次のようになります。
define(N,100) define(M,N)
この例では,M
および
N
が
100
になるように定義しています。
後で
N
の定義を変更し,新しい値を割り当てた場合,M
は,ユーザが
N
に与えた新しい値ではなく,100
の値を保持します。m4
プリプロセッサが可能な限り早い時点でマクロ名をそれらの定義テキストに展開するため,M
の値は
N
の値に置き換えられません。
全体の結果は,M
に関する限り,最初から define(M,100) のように定義した場合と同じになります。M
の値を
N
の値に置き換える場合は,次のように定義の順序を入れ替えてください。
define(M,N) define(N,100)
この時点で,M
は文字列
N
であると定義されています。
後で
M
の値が要求されると,M
は
N
に置換され,N
の値は再走査され,その時点の
N
の値に置換されます。
define
コマンドで作成したマクロ定義は,閉じカッコの次の文字を削除しません。
たとえば,次のように定義します。
Now is the time for all good persons. define(N,100) Testing N definition.
上記の例の結果は,次のようになります。
Now is the time for all good persons. Testing 100 definition.
空白行が
define
マクロを含んだ行の最後に改行文字がある結果として出力されます。
組み込みの
dnl
マクロは,次の改行文字まで後続する文字を次の改行文字を含めてすべて削除します。
空白行を削除するには,このマクロを使用してください。
たとえば,次のように指定します。
Now is the time for all good persons. define(N,100)dnl Testing N definition.
上記の例の結果は,次のようになります。
Now is the time for all good persons. Testing 100 definition.
この節では,次の情報について説明します。
define
マクロの引数の展開を遅らせるには,マクロを対応する引用符で囲みます。
引用符として使用される文字の省略時の設定は,左右の一重引用符 (`
および
'
) ですが,組み込みの
changequote
マクロを使用すれば,別の文字を指定することができます (5.3 節を参照)。
引用符で囲まれたテキストはすぐには展開されませんが,引用符自体は削除されます。
引用符で囲まれた値は,引用符を削除された状態の文字列です。
次の例を参照してください。
define(N,100) define(M,`N')
引数が収集されると,N
のまわりの引用符は削除されます。
引用符を使用すると,M
は
100
ではなく,文字列
N
として定義されます。
この例では,M
の値として
N
の値が置換されています。
したがってこの方法も,5.2 節に示した次の定義と同じ結果を得るための 1 つの方法です。
define(M,N) define(N,100)
一般規則として,m4
が何かを評価するときには,必ず 1 レベルの引用符文字を削除します。
このことは外部マクロでも同じです。
たとえば,ワード define を出力に表示するには,次のように引用符で文字を囲んでください。
`define' = 1
m4
が引用符を処理するので,ネストされたマクロには注意する必要があります。
次の例を考えてみましょう。
define(dog,canine) define(cat,animal chased by `dog') define(mouse,animal chased by cat)
cat
の定義を処理した時点では,引用符で囲まれているために
dog
は直ちに
canine
に置換されませんが,mouse
が処理される場合には,cat
(animal chased by dog
) の定義が使用されます。
この時点で,dog
は引用符で囲まれていないため,mouse
の定義は
animal chased by animal chased by canine
になります。
上記の例が,infile
というファイルに含まれている場合は,次のようになります。
% cat infile define(dog,canine) define(cat,animal chased by `dog') define(mouse,animal chased by cat) dog cat mouse % m4 infile canine animal chased by canine animal chased by animal chased by canine
既存のマクロを再定義する場合は,最初の引数 (マクロ名) を次のように引用符で囲む必要があります。
define(N,100)
.
.
.
define(`N',200)
引用符で囲まれていない場合には,2 番目の
define
マクロは
N
を検出して認識し,値を置換して次のような結果になります。
define(100,200)
m4
プログラムでは,名前だけが定義できて数値は定義できないため,この文は無視されます。
5.2.2 マクロ引数
最も簡単なマクロ処理の形式は,これまでの説明にあるように,1 つの文字列を別の (固定した) 文字列に置換することです。
ただし,所定のマクロによって異なった場所で異なった結果を得るために,マクロに引数を付けることができます。
引数がマクロの置き換えテキスト (その定義の 2 番目の引数) 内で使用される位置を示すには,n
番目の引数を示す
$
n
記号を使用してください。
たとえば,記号
$1
はマクロの最初の引数を参照します。
マクロが使用された場合,m4
は,記号を示された引数の値に置換します。
次の例を参照してください。
define(bump,$1=$1+1)
.
.
.
bump(x);
この例では,m4
は
bump(x)
文を
x=x+1
に置換します。
マクロには必要なだけ引数を付けることができますが,$
n
記号 ($1
から
$9
まで) を使用してアクセスできるのは 9 個の引数だけです。
9 番目の引数以降の引数にアクセスするには,shift
マクロを使用してください。
shift
マクロは,最初の引数を手放し,残りの引数を
$
n
記号 (2 番目の引数を
$1
に,3 番目の引数を
$2
に) に再び割り当てます。shift
マクロを 1 回以上使用すると,マクロに使用されるすべての引数にアクセスすることができます。
記号
$0
はマクロ名を返します。
引数が指定されなかった場合は,空文字列に置換されます。
このため,次のように引数を連結してマクロを定義することができます。
define(cat,$1$2$3$4$5$6$7$8$9)
.
.
.
cat(x,y,z)
この例では,cat(x,y,z)
文が
xyz
に置換されます。
対応する引数が提供されないため,この例の
$4
〜$9
の引数は null になります。
マクロを走査する時点で,m4
プログラムは,引用符で囲まれていない前の空白,タブ,および引数の中の改行文字を破棄しますが,他のすべてのホワイト・スペースは保持します。
次の例を参照してください。
define(a, "$1 $2$3")
.
.
.
a(b, c, d)
この例では,a
マクロを
b cd
となるように展開しています。
ただし,define
マクロでは,改行文字は有効です。
次の例を参照してください。
define(a,$1 $2$3)
.
.
.
a(b,c,d)
この 2 番目の例では,a
マクロは次のように展開されます。
b cd
マクロ引数はコンマで区切られます。 引数にコンマが含まれている場合には,引数の途中で終了していると誤解されないように,カッコを使用してコンマを含む引数を囲んでください。 たとえば,次の文の引数は 2 つだけです。
define(a, (b,c))
最初の引数は
a
で,2 番目の引数は
(b,c)
です。
引数内に単一のカッコを指定する場合には,一重引用符で囲んでください。
define(a,b`)'c)
この例では,b)c
が 2 番目の引数になります。
5.3 他の m4 マクロの使用
m4
プログラムは,定義済みのマクロ (組み込みマクロ) のセットを提供しています。表 5-1
ではこれらのマクロのすべてをリストし,概略を説明しています。
以下の項では,大部分のマクロとその使用法について,詳細に説明します。
コメント文字の変更 (5.3.1 項)
引用符文字の変更 (5.3.2 項)
マクロ定義の取り消し (5.3.3 項)
定義されたマクロのチェック (5.3.4 項)
整数演算の使用 (5.3.5 項)
ファイルの操作 (5.3.6 項)
出力のリダイレクト (5.3.7 項)
プログラムでのシステム・プログラムの使用 (5.3.8 項)
固有のファイル名の使用 (5.3.9 項)
条件式の使用 (5.3.10 項)
文字列操作 (5.3.11 項)
表示 (5.3.12 項)
マクロ | 説明 |
changecom( l, r) |
左右のコメント文字を l および r で表された文字に変更する。 2 つの文字は異ならなければならない。 |
changequote( l, r) |
左右の引用符文字を l および r で表された文字に変更する。 2 つの文字は異ならなければならない。 |
decr( n) |
n-1 の値を返す。 |
define( name, replacement) |
replacement
の値を持つ
name
という名称の新しいマクロを定義する。 |
defn( name) |
引用符で囲まれた
name
の定義を返す。 |
divert( n) |
出力ストリームを番号 n の一時ファイルに変更。 |
divnum |
現在アクティブな一時ファイルの番号を返す。 |
dnl |
改行文字までのテキストを削除する。 |
dumpdef( `name'[, `name'...]) |
指定したマクロの名称および現在の定義をプリントする。 |
errprint( str) |
str を標準エラー・ファイルにプリントする。 |
eval( expr) |
expr を 32 ビット算術式として評価する。 |
ifdef( `name', arg1, arg2) |
マクロ name が定義されている場合は arg1 を返し,そうでない場合は arg2 を返す。 |
ifelse( str1, str2, arg1, arg2) |
文字列
str1
および
str2
を比較する。ifelse
は,それらが一致していた場合は
arg1
の値を,そうでない場合は
arg2
の値を返す。 |
include( file)
sinclude( file) |
file
の内容を返す。
ファイルにアクセスすることができない場合は,sinclude
マクロはエラーを報告しない。 |
incr( n) |
n+1 の値を返す。 |
index( str1, str2) |
文字列
str1
内の
str2
が開始する文字位置を返し,str1
が
str2
を含まない場合は -1 を返す。 |
len( str)
dlen( str) |
str
の文字数を返す。dlen
マクロは,国際化文字の 2 バイト表現を含む文字列で動作する。 |
m4exit( code) |
code
をリターン・コードとして
m4
を終了する。 |
m4wrap( name) |
他のすべての処理を完了した後で,終了前にマクロ
name
を実行する。 |
maketemp( strXXXXX str) |
引数文字列のリテラル文字列
XXXXX
をカレントのプロセス ID に置換し,ユニークなファイル名を作成する。 |
popdef( name) |
name
の現在の定義を,pushdef
マクロでセーブされた定義に置換する。 |
pushdef( name, replacement) |
name
の現在の定義をセーブし,define
と同じ方法で
name
が
replacement
になるように定義する。 |
shift( param_list) |
パラメータ・リストの位置を左に 1 つシフトし,元のリストの最初の要素を削除する。 |
substr( string, pos, len) |
文字位置 pos で始まり,len 文字長である string の部分文字列を返す。 |
syscmd( command) |
指定されたシステム・コマンドをリターン値なしで実行する。 |
sysval |
直前に使用された
syscmd
マクロからリターン・コードを獲得する。 |
traceoff( macro_list) |
リスト内のすべてのマクロのトレースをオフにする。macro_list
が指定されなかった場合は,すべてのトレースをオフにする。 |
traceon( name) |
指定されたマクロのトレースをオンにする。name
が指定されなかった場合は,すべてのマクロのトレースをオンにする。 |
translit( string, set1, set2) |
string の中の set1 のすべての文字を set2 の対応する文字に置換する。 |
undefine( `name') |
`name' で指定されたマクロ定義を取り消す。 |
undivert( n, n[, n...]) |
指定された一時ファイルの内容を,現在の一時ファイルに追加する。 |
m4
プログラムにコメントを付ける場合には,コメント文字でコメント行を区切る必要があります。
省略時の値の左コメント文字は,番号記号 ( #
) です。
省略時の値の右コメント文字は改行文字です。
これらの文字で問題がある場合は,組み込みの
changecom
マクロを次のように使用してください。
changecom({,})
この例では左右の中カッコを新しいコメント文字にしています。
元のコメント文字に戻すには,次のように
changecom
を使用してください。
changecom(#, )
引数なしで
changecom
を使用すると,コメントは無効になります。
5.3.2 引用符文字の変更
引用符文字の省略時の値は左右の一重引用符 (`
および
'
) です。
これらの文字で問題がある場合は,組み込みの
changequote
マクロで引用符文字を,次のように変更してください。
changequote([,])
この例では,左右の大カッコ用の新しい引用符で囲む文字が作成されます。
元の引用符文字に戻すには,次のように引数なしで
changequote
を使用してください。
changequote
undefine
マクロはマクロ定義を取り消します。
次の例を参照してください。
undefine(`N')
この例では,N
の定義が取り消されています。
定義を取り消すマクロ名は引用符で囲まなければなりません。undefine
を使用すれば組み込みのマクロを取り消すこともできますが,組み込みのマクロを取り消すと,マクロを復旧して使用することはできなくなります。
5.3.4 定義されたマクロのチェック
組み込みの
ifdef
マクロは,現在マクロが定義されているかどうか確認します。ifdef
マクロは 3 つの引数を受け入れます。
最初の引数が定義されている場合,ifdef
の値は 2 番目の引数です。
最初の引数が定義されていない場合,ifdef
の値は 3 番目の引数です。
3 番目の引数がない場合,ifdef
の値は空になります。
5.3.5 整数演算の使用
m4
プログラムは,整数のみの演算を可能にする,次の組み込み関数を提供しています。
incr |
数値引数を 1 つ増分させる |
decr |
数値引数を 1 つ減少させる |
eval |
算術式を評価する |
たとえば,その値がいつでも
N
より 1 大きくなるような,変数
N1
を作成することができます。
define(N,100) define(N1,`incr(N)')
eval
関数を使用して,次の演算子を含む式を評価することができます。
優先順位の高い順にリストされています。
単項 + (プラス),単項 - (マイナス)
**
または
^
(べき乗)
*
,/
,%
(剰余)
+
,-
==
,!=
,<
<=
,>
,>=
!
(論理否定)
&
または
&&
(論理 AND)
|
または
||
(論理 OR)
カッコを使用して,必要な位置で演算をグループ化してください。
式のすべてのオペランドは
1>0
などの真の関係の数値は 1 で,偽は 0 (ゼロ) です。eval
の精度は 32 ビットです。
たとえば,M
を
2==N+1
として定義するには,次のように
eval
を使用してください。
define(N,3) define(M,`eval(2 == N+1)')
テキストが単純で,マクロ名のインスタンスを含まない場合を除いて,マクロを定義するテキストは引用符文字で囲んでください。
5.3.6 ファイルの操作
入力に新しいファイルを挿入するには,組み込みの
include
マクロを次のように使用してください。
include(myfile)
この例では,include
コマンドの場所に
myfile
の内容が挿入されています。
組み込まれたファイルが読み込まれると,m4
は,それが最初の入力ファイルの一部であるかのように,そのファイルの中にマクロがあるか走査します。
include
マクロで指定されたファイルにアクセスできない場合には,致命的なエラーが発生します。
エラーを回避するには,代わりのフォーマットである
sinclude
(silent inlcude) を使用してください。
指定されたファイルにアクセスできない場合にも
sinclude
マクロは,エラーなしで続きます。
5.3.7 出力のリダイレクト
処理中に
m4
の出力を一時ファイルにリダイレクトし,収集したデータをコマンドで出力することができます。m4
プログラムは,1〜9 までの番号を付けて最大 9 個の一時ファイルを保守することができます。
出力をリダイレクトするには,以下の例のように
divert
マクロを使用します。
divert(4)
このコマンドが出現すると,m4
はその出力を一時ファイル 4 の終わりまで書き出し始めます。
出力点を 1〜9 以外の一時ファイルにリダイレクトすると,m4
プログラムは出力を破棄します。
この機能を利用して,m4
は入力ファイルの一部を割合することができます。
出力を標準出力ストリームに返すには,divert(0)
または引数なしで
divert
を使用してください。
処理の終了時に,m4
は,すべてのリダイレクトされた出力を,標準出力ストリームに書き出し,番号順に一時ファイルから読み込んで,一時ファイルを削除します。
処理の終了前にも,番号順にすべての一時ファイルから情報を取り出したい場合には,引数なしで組み込みの
undivert
マクロを使用します。
特定の順番で一時ファイルを選んで取り出す場合には,引数を付けて
undivert
を使用します。undivert
を使用した場合,m4
は回収した一時ファイルを破棄し,回収した情報に対してはマクロを探索しません。
undivert
の値は,切り替えられたテキストではありません。
組み込みの
divnum
マクロは,現在使用中の一時ファイルの番号を返します。
divert
マクロで出力ファイルを変更しない場合,m4
は,すべての出力を一時ファイル 0 (ゼロ) にいれます。
5.3.8 プログラムでのシステム・プログラムの使用
組み込みの
syscmd
マクロを使用すると,プログラム内から任意のオペレーティング・システムのプログラムを実行することができます。
システム・コマンドが情報を返す場合は,その情報は
syscmd
マクロの値になり,そうでない場合はマクロの値は空になります。
たとえば次のように使用します。
syscmd(date)
プログラムから固有のファイル名を作成するには,組み込みの
maketemp
マクロを使用してください。
マクロの引数にリテラル文字列
XXXXX
がある場合,m4
は
XXXXX
を現在の処理プロセス ID に置換します。
たとえば次のように指定します。
maketemp(myfileXXXXX)
現在のプロセス ID が 23498 である場合は,この例は
myfile23498
を返します。
この文字列を使用して一時ファイルに名称を付けることができます。
5.3.10 条件式の使用
組み込みの
ifelse
マクロは条件付きのテストを実行します。
最も簡単な形式は次のような形式です。
ifelse(a,b,c,d)
この例では,2 つの文字列
a
および
b
を比較します。a
および
b
が同一である場合,ifelse
は文字列
c
を返します。
異なる場合は,文字列
d
を返します。
たとえば,compare
というマクロを次のように定義して 2 つの文字列を比較することができます。
2 つの文字列が同じである場合は
yes
を,異なる場合は
no
を返します。
define(compare, `ifelse($1,$2,yes,no)')
引用符を付けることによって,ifelse
の評価が早すぎないようにします。
4 番目の引数を省略すると,空として処理されます。
ifelse
マクロは引数をいくつでも付けることができます。
したがって,多岐選択決定機能の限定された形式を提供します。
たとえば次のようになります。
ifelse(a,b,c,d,e,f,g)
この文は次のフラグメントと論理的に同じです。
if(a == b) x = c; else if(d == e) x = f; else x = g; return(x);
最後の引数が省略された場合は,その結果は空になります。
5.3.11 文字列操作
組み込みの
len
マクロは,その引数を構成する文字列のバイト長を返します。
たとえば,len(abcdef)
は 6 で,len((a,b))
は 5 になります。
組み込みの
dlen
マクロは,文字列で表示可能な文字列の長さを返します。
国際化の使用環境では,2 バイト・コードを 1 文字として表示するものもあります。
したがって,文字列に 2 バイトの国際文字コードが含まれる場合,dlen
の結果は
len
の結果と異なります。
組み込みの
substr
マクロは,指定された文字列 (最初の引数) から部分文字列 (2 番目の引数によって指定された文字位置で始まる) を返します。
3 番目の引数は,返される部分文字列のバイトの長さを指定します。
たとえば次のように指定します。
substr(Krazy Kat,6,5)
この例では,文字列 "Krazy Kat" の文字位置 6 から始まる 3 つの部分文字列 "Kat" を返します。 文字列の最初の文字は位置 0 (ゼロ) にあります。 3 番目の引数が省略されるか,あるいはこの例のように文字列の長さが 3 番目の引数が必要とする長さに満たない場合,残りの文字列が返されます。
組み込みの
index
マクロは,部分文字列 (2 番目の引数) が始まる文字列 (最初の引数) 内でバイト位置すなわちインデックスを返します。
部分文字列がない場合,index
は -1 を返します。
substr
を使用すると,文字列の起点は 0 (ゼロ) になります。
次の例を参照してください。
index(Krazy Kat,Kat)
この例は 6 を返します。
組み込みの
translit
マクロは,1 対 1 の文字置換すなわち字訳を実行します。
最初の引数は,処理される文字列です。
2 番目と 3 番目の引数は文字のリストです。
文字列で見つけることのできる 2 番目の引数からの文字の各インスタンスは,3 番目の引数からの対応する文字に置換されます。
次の例を参照してください。
translit(the quick brown fox jumps over the lazy dog,aeiou,AEIOU)
この例では次のように返されます。
thE qUIck brOwn fOx jUmps OvEr thE lAzy dOg
3 番目の引数が 2 番目の引数より短い場合,3 番目の引数に存在しない 2 番目の引数に対する文字は削除されます。 3 番目の引数がない場合,2 番目の引数に表示されているすべての文字が削除されます。
注意
substr
,index
,およびtranslit
マクロは,1 バイトと 2 バイトの表示可能文字の区別を行わないので,国際化コードを使用している場合に予期しない結果を返すことがあります。
組み込みの
errprint
マクロは,引数を標準エラー・ファイルに書き込みます。
たとえば次のように指定します。
errprint (`error')
組み込みの
dumpdef
マクロは,現在の名前および引数として指定された項目の定義をダンプします。
名前は必ず引用符で囲まなければなりません。
引数を指定しない場合,dumpdef
はすべての現在の名前および定義を表示します。dumpdef
マクロは,標準エラー・ファイルに書き込みます。