Initialization scripts of modules can be implemented as loadable classes. So it is legal to implement initialization scripts as pnuts.lang.Executable implementation classes, but it is more common to implement them as pnuts.ext.ModuleBase subclass.
For example, let's define "my_module" module in Java. we define my_module.init class that inherits pnuts.ext.ModuleBase. public Object execute(Context) method defines the function hello() in the "my_module" package
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;
}
}
Note that the symbol "hello" is an interned String, since pnuts.lang.Package manages symbols as interned String objects.
The easiest way to define dependency on other modules is to override ModuleBase.getRequredModules(). The following example define my_module module that depends on "pnuts.lib" and "pnuts.io" 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"};
}
...
}
For example, the following initialization script defines a module that has "functional" module as a sub-module.
package my_module;
import pnuts.lang.Context;
import pnuts.ext.ModuleBase;
public class init extends ModuleBase {
protected String[] getSubModules(){
return new String[]{"functional"};
}
...
}
Dependency or submodule can be defined at run time by using appropriate modules in ModuleBase.execute(Context) method, as the following steps.
For example, the following code defines "my_module" module that depends on "pnuts.lib" module and "pnuts.io" module and has a sub-module "functional" 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(); // reset the module list
context.usePackage("pnuts.lib");
context.usePackage("pnuts.io");
...
}
}
The initialization script of a module can be specified in META-INF/pnuts/module/moduleName. For example, java.net module's initialization script is defined at the 1st line of META-INF/pnuts/module/java.net, as follows.
org/pnuts/java_net/init
Autoloading is used in order to minimize start-up time of modules.
Package.autoload() method can be used to autoload Pnuts scripts and loadable classes. pnuts.ext.ModuleBase class provides more convenient methods for autoloading.
To autoload Pnuts scripts (and loadable classes), ModuleBase.autoload() can be used. For example, the following code defines the name 'hello', which is expected to be defined in the script "my_module/hello", but the script is not loaded until the name is first used.
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;
}
}
To autoload functions implemented in Java, ModuleBase.autoloadFunction() method can be used. For example, the following class defines the initialization of my_module module. When the symbol 'hello' is first dereferenced, org.pnuts.my_module.hello class is loaded and instantiated by the default constructor and returned to Pnuts interpreter.
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() method defines the prefix of the class names (the default prefix is null). In this example, the class name is constructed from the prefix "org.pnuts", module name "my_module", and the function name "hello". If getPrefix() method is not defined, the prefix is an empty string.
To autoload Java classes, ModuleBase.autoloadClass() can be used. For example, the following code defines two names 'Vector' and 'Hashtable', which will be the corresponding Class objects, but the classes are not loaded until the names are first used.
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;
}
}
When pnuts.lib module is being used, the formatMessage() function could be used to show localized message. But how can be do that in a module which does not depend on pnuts.lib module?
If the initialization code is a subclass of pnuts.ext.ModuleBase, ERROR() function can be used in the functions of the module.
This function finds a message resource from "errors.properties", format the error message in the same way as formatMessage(), and then throw pnuts.lang.PnutsException with the formatted message.
if (!isFunction(obj)){
ERROR("must_be_a_function", obj)
}
must_be_a_function=A function is expected: {0}