マクロの実装を想像する

目的と経緯

マクロを実装するにあたり、マクロをある程度知ることはできた。
次はマクロを実装する裏側を想像し、それが妥当かどうか検証する。

お題

まず考えるためのお題は

(define-syntax when
  (syntax-rules ()
    ((_ pred a b)
     (if pred (begin a b)))))

としよう。

2つのフェーズ

インタプリタから見ると大きく2つのフェーズがあるように見える

  1. define-syntax を与えられて解釈して、あれこれするフェーズ
  2. define-syntax で define されたマクロが別の文脈で呼び出されたときにコードを生成するフェーズ
define-syntax を与えられて解釈して、あれこれするフェーズ

when というキーワードに対して2つのものを解釈し保持する

  1. when の書き方ルール
  2. ルールを展開結果

現時点では、まだ理解が浅いので何とも言えないが、when を置き換えルールで別のソースコードに変換するイメージか。
でも、Cのマクロとは違うとどこかで読んだので単純に置き換えモデルのではないのかも。

define-syntax で define されたマクロが別の文脈で呼び出されたときにコードを生成するフェーズ
  1. when が出現した
  2. when が定義されているマクロパターンとマッチするか判断
  3. マッチするのであればマクロを置き換える

置き換えは (when -> (if のようにソースレベルの置き換えの方がきれいかな。
(when -> インタプリタ中の if インスタンスってのは、飛ばしすぎだし無駄かも。

ふと

C++のテンプレートをうまく活用できないだろうかと考える。

マッチとは

マッチに使う情報

  • マクロ名
  • 予約語のリスト
  • Nodeのツリー

match関数は 名前と予約語と Node ツリーを比較する感じか。

マッチした場合

Nodeツリーの位置で書かれた変換表に従いソースコード変換。
その後 eval 。

テスト期

いろいろ考えていたら、まずはテストだと思いつく。
テスト期テスト期。

cppunitを使ってこんな感じで。

#define MACRO_MATCH(macroName, macro, target, matchOrNot)                                     \
{                                                                                             \
    Node* m = Macro::toNode(macro);                                                           \
    Node* t = Macro::toNode(target);                                                          \
    CPPUNIT_ASSERT_MESSAGE(matchOrNot ? macro " matches " target : macro " not match " target \
    , Macro::match(macroName, m, t) == matchOrNot);                                           \
}

#define _Y(macroName, macro, target) MACRO_MATCH(macroName, macro, target, true)
#define _N(macroName, macro, target) MACRO_MATCH(macroName, macro, target, false)

void MacroMatchTest::testNormalMatch()
{
    _Y("and", "(_ a b ...)", "(and a b c)");
    _N("and", "(_ a)"      , "(and a b)");
}

よしテストの準備は出来た。
match関数は誰か書いて(ぉ。