Pnuts API Overview

Introduction

Pnuts API is a set of classes that provides methods to use the scripting functionalities of Pnuts. No initialization step is required to use the API and it can be called at any place in a Java program. Pnuts interpreter shares the thread resource and the object heap with the Java program that call the API.

Class Description
pnuts.lang.Pnuts provides public static methods to execute scripts
pnuts.lang.Context environment of script execution
pnuts.lang.Package non-local name space
pnuts.lang.PnutsFunction represents a Pnuts function
pnuts.lang.PnutsException exception during script execution
Core classes of Pnuts API

pnuts.lang.Pnuts

pnuts.lang.Pnuts class has static methods which starts an interpreter. Pnuts.eval() and Pnuts.load() are two of the most important methods to call Pnuts script from Java.

public static Object eval ( String exp, Context context );
public static Object load ( InputStream in , Context context );
public static Object load ( Reader in , Context context );
public static Object load ( URL scriptURL , Context context );
public static Object load ( String rsrc , Context context ) throws FileNotFoundException;

The following example illustrates a simple usage of Pnuts.eval() method.

import pnuts.lang.*;
import java.math.*;

class Foo {
   public static void main(String arg[]){
       Context context = new Context();
       BigInteger bint = (BigInteger)Pnuts.eval("1<<100", context);
       ...
   }
}

pnuts.lang.Pnuts class can be instantiated only by Pnuts::parse(InputStream/Reader) methods. The instance of pnuts.lang.Pnuts can be executed by run(Context) method.

public static Pnuts parse( InputStream in ) throws ParseException;
public static Pnuts parse( Reader in ) throws ParseException;
public static Pnuts parse( Reader in, Object scriptSource, Context context) throws ParseException;
public Object run( Context context);

The following program illustrates how to parse a Pnuts script first and execute it later.

import pnuts.lang.*;
import java.io.*;

class ParseTest {
  public static void main(String a[]){
     Pnuts p = null;
     try {
        p = Pnuts.parse(new FileInputStream(a[0]));
     } catch (ParseException e1){
        ...
     } catch (IOException e2){
        ...
     }
     ...
     p.run(new Context());
  }
}

Handling Multiple Parse Errors

One of the following methods is used to handle multiple parse errors.

public static Pnuts parse( Reader in , ParseEnvironment env) throws ParseException;
public static Pnuts parse( Reader in, Object scriptSource, Context context, ParseEnvironment env) throws ParseException;
import pnuts.lang.*;
import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
	try {
	    FileReader r = new FileReader(args[0]);
	    Pnuts.parse(r, new ParseEnvironment(){
		    public void handleParseException(ParseException e) throws ParseException {
			System.out.println(e);
		    }
		});
	} catch (ParseException e){
	    throw new Error(); // never happens
	}
    }
}

Script Source

Script source is an attribute of Pnuts object, which denotes the origin of the script, usually a URL object.

public Object getScriptSource();
public void setScriptSource(Object scriptSource);

Script source information is mainly used by debugger.

pnuts.lang.PnutsFunction

PnutsFunction is another entry point into the Pnuts interpreter. It is used to call a Pnuts function from Java.

public static Object call(String name, Object args[], Context context);
import pnuts.lang.*;

class Foo {
   public static void main(String arg[]){
       Context context = new Context();
       Pnuts.eval("function foo() 100", context);
       ...
       System.out.println(PnutsFunction.call("foo", new Object[]{}, context));
   }
}
If a Java program holds a reference to PnutsFunction object, the function can be called directly by the following instance methods.
public Object call(Object[] args, Context context);
import pnuts.lang.*;

class Foo {
   public static void main(String arg[]){
       Context context = new Context();
       PnutsFunction func = (PnutsFunction)Pnuts.eval("function foo() 100", context);
       ...
       System.out.println(func.call(new Object[]{}, context));
   }
}

The following methods retrieve the function definition from a PnutsFunction object.

public String unparse(int narg);
The body of function definition.
public String[] getImportEnv(int narg);
Array of imported Java package names and class names
public Package getPackage();
Package in which the function is defined.
function f(n) n * n

f.unparse(1) ==> "function f(n) n * n"
f.getPackage() ==> package ""
f.getImportEnv(1) ==> ["java.lang.*", "*"]

See also "Defining Functions in Java".

pnuts.lang.Package

Package is a name space which can be used for preventing name conficts among scripts. Each Package has a symbol table to store non-local names.

Package Hierarchy

Package class has consturctors that takes the package name and the parent Package as the parameters. A package hierarchy is implicitly or explicitly created by calling a constructor.

public Package();
public Package(String name);
public Package(String name, Package parent );

Root package is the topmost package in a package hierarchy. If null is specified as the parent, it becomes a new root package. When a script file is loaded, it is executed with the root package as the current package.

When parent is not explicitly specified, the global package is used as the parent Package. pnuts command uses the global package as the default root package. As long as pnuts command is used, there is almost no need to create a new root package.

Root packages need to be created to build multiple isolated scripting environments. For each isolated scripting environment, a root package is created. Each root package builds a separate package tree. Non-local names used by each scripting environment are stored in some package of a package tree, in such a way that variables in a scripting environment do not interfere with other scripting environments.

The global package is the default root package provided by Pnuts API. It is referenced by a static field of pnuts.lang.Package class. Therefore, the global package is never reclaimed by GC. Other root packages can be reclaimed by GC if they are no longer referenced and ready for GC.

Sub-Package

The following method is used for creating a Package. If Package with the name pkgName does not exists it creates the Package. The created package is managed by the root package (of the current package) and identified by the package name.

public static Package getPackage(String pkgName , Context context);

Access to the Symbol Table

The following methods are access methods to the symbol table in a Package.

public Object get(String symbol , Context context );
public void set(String symbol, Object value , Context context );
public boolean defined(String symbol , Context context );
public void clear(String symbol , Context context );

Note that symbol in these methods must be an interned String object.

AutoloadHook

public void autoload( String symbol, String file, Context context );
public void autoload( String symbol, AutoloadHook hook );
If the variable is undefined in the symbol table when Package.get(String, Context) method is called, and a AutoloadHook for the variable has been registered by autoload() method, the AutoloadHook is invoked. When either no AutoloadHook is registered for the variable or the variable was not defined by invoking the AutoloadHook, Package.get(String, Context) method does the same thing for the parent Package.

Modules and Packages

When Context.usePackage() method is called to install a module, the module initialization script is executed. A sub-package bound to the module is created if it does not exist, and the module functions are stored in the package.

Package objects maintains a list of exported names, when the package is used as a module. Module initialization scripts should register the exported names with Package.export() method.

public void export( String symbol );

If no names are exported by the initialization script of a module, all named functions defined in the package is implicitly exported.

When a module is implemented with pnuts.ext.ModuleBase class, it does not have to call export() method directly, because autoload() method or a autoloadFunction() method calls export() method internally.

pnuts.lang.Context

A Context represents an internal state in the Pnuts runtime environment. It holds the following information.

A Context object need to be created when evaluating a script.

public Context();
public Context(Package pkg);
public Context(Package pkg, Object source);
public Context(Context template);

The default constructor of Context creates a Context object with the global Package. The constructor Context(Package anPackage) creates a Context and set the default Package.

A Context object can be passed around several interpreters specifying the object as the parameter of Pnuts.eval(), Pnuts.load(), or Pnuts.loadFile() method. Also a context can be used from different threads at the same time.

Clones

When one of the built-in functions, eval(),load(), require(), and loadFile() is called from a script, a clone of the Context being used is made and passed to the function.

When one of the built-in functions, load(), require(), and loadFile() is called, the specified script is executed setting the current package to the top of the package hierarchy (usually the global package).

Some of the attributes are shared among the clones, and some of them are copied.

Attribute What happens when a clone is made
The classloader associated with the context shared
Modules added by use() shared
The list of files loaded by load() shared
OutputStream for messages copied
Context-local Variables copied
Implementation object copied
Configuration copied
Encoding copied
Imported Java package list reset
Current package reset

If all attributes need to be copied, use the constructor Context(Context) instead of clone().

OutputStream for Messages

As shown below, three kinds of output stream can be specified for a Context.

Context-local Variables

Context-local variable is a kind of environment variable bound to a context.

public void set (String symbol , Object value);
public Object get (String symbol );

Note that the parameter symbol must be an interned string.

Context-local variables can be accessed with field access expression on a Context object.

context = getContext()
context.foo       ==> null
context.foo = date()

Encoding

Context may declare a script encoding with setScriptEncoding() method.

public void setScriptEncoding (String encodingName );
public String getScriptEncoding ();

ClassLoader

A class loader can be associated with the context. It is used to resolve class names, and finds scripts when load() function is used.

public void setClassLoader (ClassLoader loader );
public ClassLoader getClassLoader ();

Implementation Object

See "Customizing the Interpreter's Implementation".

public void setImplementation (Implementation impl );
public Implementation getImplementation ();

Configuration

Configuration defines how to find method/field candidates, and semantics of field access.

context . setConfiguration ( Configuration impl )
context . getConfiguration ( )

See "Customizing the Behavior of Java API Access".

Modules

use() function registers a module to the executing context. The registered modules are managed and shared by a family of Context clones.

public boolean usePackage (String name );
public String[] usedPackages ();
public void clearPackages ();

usePackage() registers a module to the context and executes the initialization script of the module.

usedPackages() returns the list of registered module names.

clearPackages() resets the module list back to empty.

Difference between eval() and Pnuts::eval()

eval(expr)
Evaluates expr with a copy of the current Context
eval(expr, context)
Evaluates expr with a copy of context
Pnuts::eval(expr)
Evaluates expr with a newly created Context
same as Pnuts::eval(expr, Context())
Pnuts::eval(expr, context)
Evaluates expr with context

When a Context object is passed to Pnuts::eval(expr, context), import() and some other functions can modify the caller's context. On the other hand, the original Context object can not be modified by primitive function eval(), because it uses a copy of a Context.

pnuts.lang.PnutsException

When an exception is thrown during executing script through Pnuts.eval(), Pnuts.load(), or Pnuts.loadFile() method, the exception is propagated to the caller of the methods.

The exception is encapsulated in a pnuts.lang.PnutsException object and can be retrieved with the PnutsException.getThrowable() method.

public Throwable getThrowable();
import pnuts.lang.*;
import java.io.*;

class Foo {
   public static void main(String arg[]){
      try {
         Object ret = Pnuts.eval(arg[0], new Context());
	 System.out.println("ret = " + ret);
      } catch (PnuteException e){
	 if (e.getThrowable() instanceof IOException){
	   e.printStackTrace();
	 } else {
	   System.out.println("caught: " + e);
	 }
      }
   }
}

getScriptSource() returns the source of the script where the error occured, which is usually a URL object. getLine() returns the line number where the error occured.

public Object getScriptSource();
public int getLine();

To obtain the positional information of an error thrown by Pnuts.run(Context), the script source has to be set using Pnuts.setScriptSource() method before executing the script.

e.g.
import pnuts.lang.*;
import java.io.*;

class Foo {
   public static void main(String arg[]) throws IOException {
      try {
         File file = new File(arg[0]);
         FileInputStream in = new FileInputStream(file);
         Pnuts expr = Pnuts.parse(in);
	 expr.setScriptSource(file);
	 expr.run(new Context());
      } catch (PnutsException e){
         System.out.println(e.getScriptSource() + ": " + e.getLine());
      }
   }
}