Nemerle入門Ⅷ ～マクロ（基礎），マクロ（構文拡張の基礎）～

###マクロとは何？
マクロとは，関数とあまり違いがないように見えますが，色々違います．それをこれから見ていきます．
###マクロで何ができるの？
コンパイル時評価，新しいシンタックスの作成，コード記述量の短縮ができます．
##事始め
Nemerleの現在の仕様では，マクロは他dllのもののみを使うことができ，同一dllでマクロを作成・利用はできないので，プロジェクトソリューションにマクロライブラリプロジェクトを追加して，メインプロジェクトの参照に追加する必要があります．
メインプロジェクトにマクロライブラリの参照を追加し終わったら，早速，マクロライブラリの方にマクロを書いていきます．マクロは`static class`(`module`)・`class`に書くことはできません．`namespace`の下に書かなければいけません．マクロプロジェクトを作れば，適切な場所にマクロができていると思うので，そこまで思慮しなくてもいいと思います．
##コンパイル時評価のマクロ
では，マクロを書きます．

```nemerle:Macro1.n
public macro HogeMacro() {
  WriteLine("Hello, Macro!");
  <[()]>;
}
```
`<[()]>;`はあとで詳しい話をしますが，Macroは`PExpr`型の値(?)を必ず返すので，必要です．
そうしたら，メインプロジェクトからマクロを呼び出すようにします．`using`でマクロの名前環境をユージングします．後々説明しますが，この場合は，`_ =`は不要です．

```nemerle:Main.n
using <MacroNamespace>;

Main() : void {
  HogeMacro();
  _ = ReadLine();
}
```
実行してみましょう．しかし，何も表示されません．しかし，VSの「出力ウィンドウ」の「ビルド」を見ると，このような感じに出力されています．
>------ ビルド開始: プロジェクト:Macro Project, 構成:Debug Any CPU ------
	Macro Project -> DLL Name

>ビルドに成功しました。

>Time Elapsed 0:00:01.29
------ ビルド開始: プロジェクト:Main Project, 構成:Debug Any CPU ------
	Hello, Macro!
	Main Project -> EXE Name

>ビルドに成功しました。

>Time Elapsed 0:00:00.90
========== ビルド: 正常終了または最新の状態 2、失敗 0、スキップ 0 ==========

MainProjectのビルド時に"Hello, Macro!"と出力されているのがわかるでしょうか．このように，マクロ内でのコードはコンパイル時に呼び出されたときに評価されます．では，マクロ内で実行時評価をするにはどうすればいいでしょうか．
##PExpr
`<[]>`でPExpr型の値を表現します．PExpr型は簡単に言うと式木(`Expression型`)のようなもので，処理を表します．どのように処理を記述するか，ですが，普通に書けばよろしいです．

```nemerle:PExpr
<[
Console.WriteLine("TEST!");
def n = 12;
12;
]>
```
返し値も普通に表現できます．
空(`void`)のPExprは，`()`で表現すれば，おｋです．

```nemerle:()
<[()]>
```
##マクロの返すPExprの振る舞い
マクロは必ずPExprを返さなければなりません．そして，そのPExprは，呼び出し元のコードに置換されます．どういうことかを説明すると，

```nemerle:replace_PExpr
public macro RandMacro() {
  <[
    def r = Random().NextDouble();
    Console.WriteLine(r);
    r;
  ]>
}

public void Main() : void {
  def n = RandMacro();
}
```
これが，コンパイル時に

```nemerle:replaced_Macro
public void Main() : void {
  def n = {
    def r = Random().NextDouble();
    Console.WriteLine(r);
    r;
  }
}
```
のように，RandMacro()がRandMacroの返り値に置換されます．(実際に，ILSpy等で確認してみるのもいいでしょう．)このように，返し値の`PExpr`に処理の記述をすることによって，呼び出し元のコードを変更，すなわち，実行時評価することができます．
**コンパイル時にソースコードを組み替えたり，予め計算したりして，コードの視認性を上げる・処理を高速化させる**，これが**マクロ**です．
##<[]>の中から<[]>外の変数を使う
`PExpr`の中から`PExpr`外の変数を利用するには，`$`を変数名の前に付ける必要があります．

```nemerle
public macro HogeMacro(d) {
  def s = System.Math.Sqrt(d);
  <[$s]>;
}
```
このようなマクロを書くことによって,`Sqrtメソッド`は，コンパイル時に評価されるので，実行時の計算数が減ります．

##引数
まず，コンパイル時評価でやってみましょう．

```nemerle
public macro HogeMacro(d : double) {
  System.Math.Sqrt(d) |> WriteLine;
  <[()]>;
}

public void Main() : void {
  HogeMacro(16.0);
}
```
コンパイル時に，"4"と出力されるでしょう．

```nemerle
public void Main() : void {
  def n : double = 12
  HogeMacro(n);
}
```
上のコードをコンパイルしてみましょう．コンパイルエラーが出ます．コンパイル時評価で使われる引数は直接コンパイル時に解釈できる定数を代入しなければならないようです．
***
次に，実行時評価でやってみます．

```nemerle
public macro HogeMacro(d) {
  <[System.Math.Sqrt($d)]>;
}
```
引数でも`$`を付けます．
実行時評価は，引数が，コンパイル時に確定できなくても大丈夫です．

（あんまり，筆者もココらへん把握しきれてない）
#シンタックスの拡張
シンタックスの拡張では`syntax`構文を使います．

```nemerle:newif
public macro newifMacro(cond, e1, e2)
syntax ("newif", "(",cond, ")", e1, "else", e2){
  <[
    match($cond) {
      | true => $e1
      | false => $e2
    }
  ]>
}

newif(condition) {
  code1
} else {
  code2
}
```
if文の例ですが，見てわかるように，とてもわかりやすく記述することができます．
'{}'が`syntax`の中に定義されてないから，複数行かけないんじゃないの？と思われそうですが，前述したような，「ブロック」という機能があるので，特記しなくても，複数行でも一行でも大丈夫です．
##Optional
```nemerle
newif() 
  WriteLine("True")
else
  WriteLine("False")
```
上のように，条件の括弧の中を省略した場合，自動的に，Trueのコードが実行されるようにします．そんな時に，Optionalを使います．

```nemerle
public macro newifMacro(cond, e1, e2)
syntax ("newif", "(", Optional(cond), ")", e1, "else", e2){
  def cond = if(cond == null) true else cond;
  <[
    match($cond) {
      | true => $e1
      | false => $e2
    }
  ]>;
}
```
省略された場合，その引数は`null`になります．これは，コンパイル時に決まるので，コンパイル時評価で分岐ができます．上記の例では，`cond`が`null`だったときに，`cond`を`true`に再定義しています．
（defで，変数の再定義ができ，その変数の型等も変えることができます．）
##マクロ名
今までの例では，`newif`構文のマクロ名を`newifMacro`としてきましたが，カッコ悪いので，`newif`という名前にしましょう．といっても，そのまま書くと，コンパイラは構文なのかマクロ名なのか分からずエラーを出すので，構文と被ってしまう時には，先頭に`@`を付けます．これは，マクロに限った話ではなく，関数や変数の定義の時にも使えます．
##構文の重複
今までで定義した，newif文は，`newif ( <cond> ) { <e1> } else { <e2> }`という，`else`まで記述しなければいけない面倒なif文です．なので，`else`のいらないパターンも作ります．

```nemerle
macro newifMacroWithoutElse(cond, e)
syntax("newif", "(", cond, ")", e) {
  <[
    match(cond) {
      | true => e;
    }
  ]>;
}
```
普通に書けば大丈夫です．
#〆
今回は，マクロの基礎・構文の拡張の基礎について紹介しました．
次回からはクラス・メソッドを拡張するマクロについても紹介しますが，どんな感じに説明するか考え中です．．．