モジュールの初期化スクリプトは、load可能なクラスで実装することができます。 したがって、pnuts.lang.Executableインタフェースの実装クラスで実現できます。 しかし、通常はpnuts.ext.ModuleBaseのサブクラスで実装されることが多いです。
たとえば、"my_module"モジュールのを定義してみましょう。デフォルトの初期化スクリプトは、"my_module/init" なので、my_module.initクラスをpnuts.ext.ModuleBaseのサブクラスとして定義します。public Object execute(Context)メソッドで、"my_module"パッケージに関数helloを定義します。
package my_module;
import pnuts.ext.ModuleBase;
import pnuts.lang.Context;
import pnuts.lang.Package;
public class init extends ModuleBase {
public Object execute(Context context){
Package pkg = Package.getPackage("my_module", context);
pkg.set("hello".intern(), new hello(), context);
return null;
}
}
Package.set()メソッドに渡すシンボルはintern()されたStringオブジェクトでなければならないことに注意して下さい。
モジュールが別のモジュールに依存している場合(モジュール実装のために他のモジュールが必要になる場合)、 ModuleBase.getRequiredModules()メソッドでモジュール名の配列を返すように定義します。
たとえば、次の例は、pnuts.libとpnuts.ioに依存するmy_moduleモジュールを定義する例です。
package my_module;
import pnuts.lang.Context;
import pnuts.ext.ModuleBase;
public class init extends ModuleBase {
protected String[] getRequiredModules(){
return new String[]{"pnuts.lib", "pnuts.io"};
}
...
}
たとえば、次の例は、functionalモジュールをサブモジュールとして定義します。
package my_module;
import pnuts.lang.Context;
import pnuts.ext.ModuleBase;
public class init extends ModuleBase {
protected String[] getSubModules(){
return new String[]{"functional"};
}
...
}
getRequiredModules()やgetSubModules()でモジュールのリストを定義するのではなく、execute()メソッドで Context.usePackage()を呼び出すことで、サブモジュールや依存するモジュールを実行時に定義することができます。
手順は、次のようになります。
たとえば、次の例は、functionalモジュールをサブモジュールとし、pnuts.libとpnuts.ioに依存するmy_moduleモジュールを定義する例です。
package my_module;
import pnuts.lang.Context;
import pnuts.ext.ModuleBase;
public class init extends ModuleBase {
public Object execute(Context context){
context.usePackage("functional");
context.clearPackages(); // モジュール・リストのリセット
context.usePackage("pnuts.lib");
context.usePackage("pnuts.io");
...
}
}
META-INF/pnuts/module/モジュール名 というリソースファイルの一行目に初期化スクリプトの名前を指定することができます。 たとえば、java.netモジュールの初期化スクリプトは、META-INF/pnuts/module/java.netというファイルの一行目で、
org/pnuts/java_net/initのように指定されています。
Autoloadはモジュールの初期化のためにかかる時間を小さくするために使われます。
pnuts.lang.Packageクラスのautoload()メソッドは、Pnutsのスクリプト、またはLoad可能なクラスをautoloadするために利用できます。しかし、ここで説明するpnuts.ext.ModuleBaseクラスは、autoloadを行うためのもっと使いやすいメソッドをいくつか提供しています。
Pnutsのスクリプト、またはLoad可能なクラスをautoloadするにはModuleBase.autoload()メソッドを使います。 次の例は、my_module/hello.pnutにhello()という関数が定義されているとして、その関数が使われたときにはじめてスクリプトをロードするように設定しています。
package my_module;
import pnuts.lang.Context;
import pnuts.ext.ModuleBase;
public class init extends ModuleBase {
static String[] files = { "my_module/hello" };
static String[][] functions = {
{ "hello" }
};
public Object execute(Context context){
for (int i = 0; i < files.length; i++){
autoload(functions[i], files[i], context);
}
return null;
}
}
PnutsFunctionのサブクラスとして定義されたPnuts関数をautoloadするには、ModuleBase.autoloadFunction()メソッドを使います。 たとえば、次のクラスはmy_moduleモジュールの初期化処理を定義しています。シンボル'hello'の値を初めて参照したとき、org.pnuts.my_module.hello クラスが読み込まれて、デフォルト・コンストラクタでインスタンス化され、Pnutsインタプリタに返されます。
package my_module;
import pnuts.lang.Context;
import pnuts.ext.ModuleBase;
public class init extends ModuleBase {
static String[] javaFunctions = { "hello" };
protected String getPrefix(){
return "org.pnuts";
}
public Object execute(Context context){
for (int i = 0; i < javaFunctions.length; i++){
autoloadFunction(javaFunctions[i], context);
}
return null;
}
}
getPrefix()メソッドは、クラス名のプリフィックスを定義します(デフォルトはnull)。 この例では、クラス名は、プリフィックス"org.pnuts"、モジュール名 "my_module"、関数名 "hello"から"org.pnuts.my_module.hello"というクラス名が構成されます。 getPrefix()メソッドを定義しない場合は、クラス名のプリフィックスが空文字になります。
クラス名(短い名前)をはじめて参照したときにクラスをロードするには、ModuleBase.autoloadClass()メソッドを使います。たとえば、次の例は、VectorとHashtableという名前を定義しています。
package my_module;
import pnuts.lang.Context;
import pnuts.ext.ModuleBase;
public class init extends ModuleBase {
public Object execute(Context context){
for (int i = 0; i < javaFunctions.length; i++){
autoloadClass("java.util", "Vector", context);
autoloadClass("java.util", "Hashtable", context);
}
return null;
}
}
pnuts.libモジュールが使われている場合は、関数formatMessage()が利用できますが、pnuts.libモジュールに依存しないモジュールで同じことをするにはどうしたらよいでしょう?
初期化コードがpnuts.ext.ModuleBaseのサブクラスである場合、モジュールの関数定義の中で関数ERROR()を利用できます。
この関数は、"errors.properties"ファイルからメッセージ・リソースを見つけ、関数formatMessage(),と同じやりかたでメッセージをフォーマットし、pnuts.lang.PnutsExceptionを送出します。
if (!isFunction(obj)){
ERROR("must_be_a_function", obj)
}
must_be_a_function=A function is expected: {0}