# Extensibility

Occasionally functionality may be desired that is not easily accomplished at the Scheme level. A new first-class type may be desired, or efficient access to a Java library. &SISC; provides a simple API for such modifications.

## Adding Types

A Scheme value is represented in &SISC; as a subclass of the abstract Java class `sisc.data.Value`.

### External Representation of Values

In order to be able to display the value in the Scheme system, all `Value`s must implement the `display` method:

> public `display` `sisc.io.ValueWriter` `writer`

> > Uses the various output methods of `sisc.io.ValueWriter` to construct an external representation of the given Value suitable for output from the `display` Scheme function.

If the programmer desires the Value to have a different representation when written with `write`, the `write` method must be overridden. If it is not, the output of `display` is used for `write` as well.

> public `write` `sisc.io.ValueWriter` `writer`

> > Uses the various output methods of `sisc.io.ValueWriter` to construct an external representation of the given Value suitable for output from the `write` Scheme function. If not implemented, the output constructed the by the `display` method is used as output from `write`.

Finally, if the external representation of a new Value is likely to be long, the programmer should implement the `synopsis` method, which generates a summary representation of the value.

> public `String` `synopsis` `int` `limit`

> > Returns approximately `limit` characters from the printable representation of this value as if returned by `write`. This method is used for displaying the value in error messages where the entire representation may be superfluous.

The `sisc.io.ValueWriter` type that is passed as an argument to both `display` and `write` contains a number of methods to generate the representation. First there are several methods for appending Java Strings, characters, and SISC Values. In each, the called `ValueWriter` is returned, to allow for easy chaining of calls.

> public `sisc.io.ValueWriter` `append` `char` `c`

> > Appends a single character to the external representation.

> public `sisc.io.ValueWriter` `append` `String` `s`

> > Appends the contents of a Java String to the external representation.

> public `sisc.io.ValueWriter` `append` `sisc.data.Value` `v`

> > Appends the contents of a Scheme value to the external representation. The value is converted to an external representation using the print style with which the `ValueWriter` was constructed (for example, `display` or `write`).

In addition to the above `append` methods, the programmer may wish to force `display` or `write` rather than use the same method as the `ValueWriter`. To do this, one can call the `display` or `write` methods on the `ValueWriter`.

> public `display` `sisc.data.Value` `v`

> > Appends the external representation of the given value as returned by `display`.

> public `write` `sisc.data.Value` `v`

> > Appends the external representation of the given value as returned by `write`.

### Equality

If the one wants a Value to be comparable for any more than pointer equality, or for the concept of pointer equality to be less strict than actual pointer equality, one or more of the equality methods must be overridden.

> public `boolean` `eq` `Object` `other`

> > Returns true if another provided Java object is equal in the sense of `eq?` to this Value.

> public `boolean` `valueEqual` `Value` `other`

> > Returns true if another provided Scheme value is equal in the sense of `equal?` to this Value.

### Serializable Values

If the type that is being added will be serialized in a &SISC; heap, and it contains one or more member variables, the Value must include a default constructor (a constructor with no arguments), and implement the `deserialize`, `serialize`, and `visit` methods described in serialization.

## Adding Native Bindings

One can add native bindings to the Scheme environment by implementing a subclass of the abstract class `sisc.nativefun.NativeLibrary`. Such a subclass needs to implement four methods:

> public `String` `getLibraryName`

> > Returns the name of this library. The name should also be acceptable for use in filenames.

> public `float` `getLibraryVersion`

> > Returns the version of this library.

> public `sisc.data.Symbol[]` `getLibraryBindingNames`

> > Returns an array of the names of the bindings exported by this library. Each name is a Scheme symbol.

> public `Value` `getBindingValue` `sisc.data.Symbol` `name`

> > Returns the value of a given binding exported by this library.

### Native Procedures

It is possible to implement Scheme functions whose behavior is implemented natively in Java code. Many of &SISC;'s procedures are implemented this way. Native procedures extend the `sisc.nativefun.NativeProcedure` abstract class. Working NativeProcedure subclasses must implement the `doApply` method, as described below.

> public `Value` `doApply` `sisc.interpreter.Interpreter` `interp`

> > Perform the necessary computations of the NativeProcedure, returning a Value as the result of the procedure call. The arguments to the procedure can be found in the value rib array field, `vlr`, of the Interpreter passed as an argument. The number of arguments to the procedure can be found from the length of the array (`vlr.length`).

If the native procedure wishes to raise an error, it may do so by throwing any Java runtime exception (subclass of `java.lang.Runtime`). For a more descriptive error, one may raise a &SISC; error using any of a number of `error` forms in `sisc.util.Util`. Consult the source of Util or inquire on the sisc-devel mailinglist for assistance.

### Fixable Native Procedures

Often it is unnecessary to have access to the full Interpreter context to implement a native procedure. If the arguments to the procedure are sufficient and the procedure is purely functional (causes no side effects), it is recommended that the programmer create a fixable native procedure. These native procedures may be inlined into generated code when enabled, allowing much faster execution. In addition, the fixable native procedure interface is simpler to use.

The `FixableProcedure` abstract class consists of five methods which may or may not be subclassed. These five methods correspond to the case of calling the procedure with no, one, two, three, and more than three arguments respectively. Not overriding one of these methods will cause a call to the fixable procedure to throw the invalid number of arguments error to the caller.

> public `Value` `apply`

> > Perform the necessary computations of the FixableNativeProcedure, returning a Value as the result of the procedure call. No argument variant.

> public `Value` `apply` `Value` `v1`

> > Perform the necessary computations of the FixableNativeProcedure, returning a Value as the result of the procedure call. One argument variant.

> public `Value` `apply` `Value` `v1` `Value` `v2`

> > Perform the necessary computations of the FixableNativeProcedure, returning a Value as the result of the procedure call. Two argument variant.

> public `Value` `apply` `Value` `v1` `Value` `v2` `Value` `v3`

> > Perform the necessary computations of the FixableNativeProcedure, returning a Value as the result of the procedure call. Three argument variant.

> public `Value` `apply` `Value[]` `v`

> > Perform the necessary computations of the FixableNativeProcedure, returning a Value as the result of the procedure call. More than three argument variant.

### Indexed Native Libraries

In the most common case, a Library is created to define several bindings, including procedures whose implementations are in Java code. For this common case, a skeleton subclass of `NativeLibrary`, `sisc.nativefun.IndexedLibraryAdapter` is provided. The IndexedLibraryAdapter class provides implementations for all four required NativeLibrary methods, and introduces a new abstract method which must be implemented, called `construct`. In addition, the method `define` is provided.

In an indexed native library, each binding is associated with a Java `int` unique to that binding within the library. The IndexedLibraryAdapter subclass should in its constructor call `define` for each binding provided by the library, according to the contract of the method:

> public `void` `define` `String` `name` `int` `id`

> > Register the native binding with the given name, and assign it the given library-unique id.

In implementing the `getBindingValue` method of the `NativeLibrary` class, an `IndexedLibraryAdapter` will call the abstract method `construct` required by the its subclasses:

> public `sisc.data.Value` `construct` `int` `id`

> > Return an instance of the indexed binding.

Most frequently, the bindings created in an indexed library are native procedures. In such a case, a second class is created which subclasses `sisc.nativefun.IndexedProcedure`. IndexedProcedure is subclass of `NativeProcedure`. An IndexedProcedure subclass' constructor must call the superconstructor with an `int`, the unique id for that binding. That `int` is stored in the `id` field of `IndexedProcedure`. A subclass can then use the `id` instance variable to dispatch to many native procedures in the body of the `doApply` method required by native procedures.

So, typically, an IndexedNativeLibrary subclass is created whose `construct` method creates instances of IndexedProcedure subclasses. The IndexedNativeLibrary subclass its itself nested in the IndexedProcedure class which it is constructing. See the various indexed libraries in `sisc.modules` for concrete examples.

## Serialization

&SISC; provides an API for serializing the state of a running Interpreter. The &SISC; heap is a dump of the state of an Interpreter with the necessary code to implement &R5RS; Scheme, for example. In order to facilitate this serialization, &SISC; Expressions and Values can implement helper methods to define the serialization of the object. If the Expression or Value contains no internal state that need be serialized, the serialization methods may be ignored. If not, the Expression or Value must contain a default (no argument) constructor, and implement the following three methods:

> public `serialize` `sisc.ser.Serializer` `serializer` java.io.IOException

> > Serializes the contents of the Expression to the given Serialization context.

> public `deserialize` `sisc.ser.Deserializer` `deserializer` java.io.IOException

> > Sets the state of the Expression to the serialized data read from `deserializer`.

> public `boolean` `visit` `sisc.util.ExpressionVisitor` `visitor`

> > When called, the Expression should call `visitor.visit(n)` on any nested Expressions.

The `Serializer` and `Deserializer` objects implement Java's `java.io.DataOutput` and `java.io.DataInput` interfaces, respectively. This means that you can use any of the write/read functions in those interfaces to serialize the state of your Expression or Value. In addition, a number of methods are provided that are helpful for this domain.

The `ExpressionVisitor` passed to visit contains only one method, `visit`, which bears the same contract as the `visit` above. When called, an Expression would then call the ExpressionVisitor's `visit` method once for each nested Expression. This method is used during serialization and during printing to detect cycles in data and code structures.

### Deserializer methods

> public `BigInteger` `readBigInteger` java.io.IOException

> > Reads a BigInteger from the stream.

> public `BigDecimal` `readBigDecimal` java.io.IOException

> > Reads a BigDecimal from the stream.

> public `Class` `readClass` java.io.IOException

> > Reads a Java Class object from the stream.

> public `Expression` `readExpression` java.io.IOException

> > Reads a &SISC; Expression from the stream.

> public `Expression` `readInitializedExpression` java.io.IOException

> > Reads a &SISC; Expression from the stream, fully initialized. This method should only be used if fields internal to the Expression returned must be available during deserialization.

> public `sisc.data.Expression[]` `readExpressionArray` java.io.IOException

> > Reads an array of `Expression`s from the stream.

> public `sisc.data.Value[]` `readValueArray` java.io.IOException

> > Reads an array of `Value`s from the stream.

> public `SymbolicEnvironment` `readSymbolicEnvironment` java.io.IOException

> > Reads a &SISC; Symbolic Environment from the stream.

### Serializer methods

> public `void` `writeBigInteger` `BigInteger` `bigint` java.io.IOException

> > Writes a BigInteger to the stream.

> public `void` `writeBigDecimal` `BigDecimal` `bigdecim` java.io.IOException

> > Writes a BigDecimal to the stream.

> public `void` `writeClass` `Class` `clazz` java.io.IOException

> > Writes a Java Class object to the stream.

> public `void` `writeExpression` `Expression` `expr` java.io.IOException

> > Writes a &SISC; Expression to the stream.

> public `void` `writeInitializedExpression` `Expression` `expr` java.io.IOException

> > Writes a &SISC; Expression to the stream. This method should only be used if fields internal to the Expression returned must be available during deserialization.

> public `void` `writeExpressionArray` `sisc.data.Expression[]` `ary` java.io.IOException

> > Writes an array of `Expression`s to the stream.

> public `void` `writeValueArray` `sisc.data.Value[]` `ary` java.io.IOException

> > Writes an array of `Value`s to the stream.

> public `void` `writeSymbolicEnvironment` `SymbolicEnvironment` `e` java.io.IOException

> > Writes a &SISC; Symbolic Environment to the stream.