All Packages Class Hierarchy This Package Previous Next Index
java.lang.Object | +----epp.Macro
構文マクロ展開パスにおいて、抽象構文木の各ノードは マクロノードかどうかチェックされ、もしそうならば、 対応するマクロ展開オブジェクトのメソッド "call" が呼び出されます。
plug-in programmer は、標準の Java にないノードを導入したら、 それを標準の Java のノードに変換する「マクロ展開オブジェクト」のクラスを 定義しなければなりません。 また、定義したマクロ展開オブジェクトは、 Epp のメソッド "defineMacro" を 使って、マクロ展開オブジェクトのテーブルに登録しなければなりません。 例えば、 enum plug-in のソースコードでは、 次のようにして :enum という tag を持つ tree を展開する展開オブジェクトを登録しています。 (クラス EnumMacro は、クラス Macro のサブクラスです。)
extend void initMacroTable() { original(); defineMacro(:enum, new EnumMacro()); }
マクロ展開オブジェクトは、クラス Macro のサブクラスのインスタンスです。 マクロ展開オブジェクトは、 tree を引数にとり tree を返す call という名の メソッドを持ちます。
マクロ展開オブジェクトは、 decorator pattern によって 拡張することができます。 それには、 Epp の extendMacro メソッドを使います。 decorator からは、インスタンス変数 orig を通して、 オリジナルのマクロ展開オブジェクトを呼び出すことができます。 extendMacro によって拡張されていない時は、変数 orig の値は null です。
Tree の再帰的なマクロ展開は、メソッド macroExpandTree によって行なわれます。 メソッド macroExpandTree の動作は、厳密には次のように定義されます。
Symbol lastTag = null; while(true) { Symbol tag = tree.tag(); if (tag == lastTag) break; lastTag = tag; Macro func = (Macro)macroTable.get(tag); if (func != null) { tree = func.call(tree); } else { tree = tree.modifyArgs(macroExpandArray(tree.args())); } } return tree;すなわち、 tree の tag に対応するマクロ展開オブジェクトの メソッド call を呼び出し、返値の tag と、呼び出す前の tag が 等しければ、ループを抜けて返値を返します。そうでなければ、 tag が変わらなくなるまでマクロ展開を変換を繰り返します。 ノードの tag に対応するマクロ展開関数がない場合は、 デフォルトの動作として、自分自身の subtree をマクロ展開したものに置き換えた ノードを作ってそれを返します。
メソッド呼び出しと同じ文法を持つマクロの定義を容易にするための InvokeStyleMacro というクラスも用意されています。
public Macro()
public abstract Tree call(Tree tree)
public static Tree macroExpandTree(Tree tree)
plug-in プログラマーが定義する マクロ展開オブジェクトの中から、 このメソッドを明示的に呼び出しても構いませんが、 ほとんどの場合、その必要はありません。 (マクロ展開オブジェクトのメソッド call が返す tree は、 自動的に内部にマクロがなくなるまで展開される仕掛けに なっています。)
public static Tree macroExpand1(Tree tree)
public static Tree[] macroExpandArray(Tree args[])
public static void checkArgsLength(Tree tree, int argc)
public static void addTree(Symbol sym, Tree tree)
public static void addTree(Symbol sym, Tree tree){ TreeVec tvec = (TreeVec)Dynamic.get(sym); tvec.add(macroExpandTree(tree)); }
例えばある拡張構文をマクロ展開する際、 あるメソッドを現在展開中のクラスに追加したくなる場合があります。 そのような場合は、例えば次のようにします。
addTree(:beginningOfClassBody, aMethodDeclarationTree);addTree の第一引数に指定できる symbol には、現在以下のものがあります。
:beforeCurrentTypeDeclaration :afterCurrentTypeDeclaration :beginningOfClassBody :beforeCurrentClassBodyDeclaration :afterCurrentClassBodyDeclaration :beforeCurrentBlockStatement :afterCurrentBlockStatementこれらによって、現在のファイルのトップレベルへのコードの追加、 現在のクラスへのインスタンス変数やメソッドの追加、 現在の文の直前へのローカル型宣言の追加、 現在の文の直後への後処理の追加などができます。
public static Symbol genTemp(String prefix)
public static Symbol genTemp(String prefix){ return Symbol.intern(prefix + genTempCounter++); }
ここで、 genTempCounter は static 変数で、初期値は0です。 初期化は、 EPP の起動時に1度だけ行なわれます。
public static Error error(String str)
All Packages Class Hierarchy This Package Previous Next Index