マクロ
LuneScript のマクロは、コンパイル時にコード(AST)を展開・生成する強力な機能です。
定義
macro キーワードを使用して定義します。マクロ名は慣習としてアンダースコア _ で始めます。
macro _dprint( exp: __exp ) {
print( "DEBUG:", ,,exp );
}
引数
マクロの引数には、通常の型の他に、マクロ専用の型を指定できます。
__exp: 式そのものを受け取ります。最も汎用的な型です。sym: シンボル(変数名や関数名など)を受け取ります。__block: コードブロック{ ... }を受け取ります。
macro _assign( symbol: sym, val: int ) {
,,symbol = ,,val;
}
let mut val = 0;
_assign( val, 10 ); // val = 10; と展開される
Quoting (展開演算子)
マクロ本体(expand-statement)で引数を使用する場合、以下の演算子を使用して展開を制御します。
| 演算子 | 説明 |
|---|---|
,, |
引数の値をそのままコードとして展開します。 |
,,, |
引数の文字列値をシンボル名として展開します。 |
,,,, |
引数のシンボル名を文字列リテラルに変換して展開します。 |
pub macro _hello( name: str ) {
print( "Hello, ", ,,name );
}
Macro Statement
マクロ定義は、計算や準備を行う macro-statement(最初の {})と、実際にコードを生成する
expand-statement(後半部分)で構成されます。
macro-statement 内では、LuneScript の標準的な計算機能が利用でき、動的に展開するコードの内容を決定できます。さらに `{}
演算子を使用することで、コードの断片(stat 型)を変数に格納してリスト化することも可能です。
動的なコード生成の例
以下の例では、指定された数だけ一連の変数を自動定義します。
macro _GenVars( prefix: str, count: int ) {
{
// macro-statement: 展開するコードのリストを作成
let mut stats: List<stat> = [];
for i = 1, count {
let varName = "%s%d" (prefix, i);
stats.insert( `{
let ,,,"v_%s"(varName)~~ = ,,i;
} );
}
}
// expand-statement: 生成したリストを一気に展開
,,stats;
}
_GenVars( "val", 3 );
// let v_val1 = 1; let v_val2 = 2; let v_val3 = 3; と展開される
print( v_val1, v_val2, v_val3 ); // 1 2 3
macro-statement 内での制約
- 利用可能な関数は LuneScript の標準関数(
print,os.clock,io.openなど)のみです。 - 同じファイル内で定義された通常の関数を呼び出すことはできません(コンパイル時にはまだその関数は存在しないためです)。
- 特殊な演算子
~~は、,,,演算子が対象とする式の終わりを明示するために使用します。
注意: Lua 予約語との衝突
マクロで展開されるコード内の変数名が Lua の予約語(end など)と衝突すると、トランスコンパイル後にエラーとなります。マクロ内部で使用する変数名には注意してください。
マクロは複雑な機能であり、乱用するとコードの可読性を損なう可能性があります。適切に使用してください。