- JavaDoc

# Jep Java

- Introduction
- Basic Usage
- Variables
- Data types
- Operators
- Functions
- Custom Functions
- BigDecimal
- Modules
- Printing
- FAQ
- Migration
- Version History

# Advanced Features

- Manipulating Expressions
- Components
- Configurable Parser
- Serialization
- Threads
- Localization
- Grammar
- Other advanced features
- Use of Design Patterns in Jep

# Custom Functions

Jep allows you to add custom functions by calling `getFunctionTable().addFunction(String name, PostfixMathCommandI function)`. Custom functions are no different from built-in functions, so you can use the source code of the built-in functions as a template. Throughout the documentation it is customary to use the abbreviation pfmc to refer to these classes.

To create a custom function, you must first create a new class which extends one of the following base classes:

UnaryFunction
  For single argument functions; the `Object eval(Object l)` must be implemented.
BinaryFunction
  For two argument functions; defines a `Object eval(Object l,Object r)` method.
NaryFunction
  For functions taking an arbitrary number of arguments; defines a `Object eval(Object[] args)` method.
PostfixMathCommand
  For functions taking an arbitrary number of arguments; uses a stack for passing arguments via the `run(Stack<Object> stack)` method.

UnaryFunction, BinaryFunction and NaryFunction are new in Jep 3.3 and are easier to implement and faster to evaluate.

### UnaryFunction and BinaryFunction

These are the simplest to implement. A simple implementation of a unary square function would be:

    import com.singularsys.jep.functions.UnaryFunction;

    public class Square extends UnaryFunction {
      public Object eval(Object l) {
        double x = ((Number) l).doubleValue();
        return x*x; // result is auto-boxed to return a Double.
      }
    }

which could be added to Jep using

    jep.addFunction("square",new Square());

Binary functions are similar but the `Object eval(Object l,Object r)` method must be implemented.

### Argument type conversion and Exceptions

The above square function will work, but it does not handle illegal values well throwing a ClassCastException if a String is passed in. Improved behaviour is achieved if an EvaluationException or its subclass IllegalParameterException is thrown. The former is used for general errors and the latter is specific for illegal arguments to the function and generates a standard error message. An improved version of square would be

    import com.singularsys.jep.functions.UnaryFunction;

    public class Square extends UnaryFunction {
      public Object eval(Object l)  throws EvaluationException {
        // if incorrect type throw an exception
        // specifying position in argument list, expected type and supplied value.
        if(!(l instanceof Number))
          throw new IllegalParameterException(this,0,Number.class,l);
        double x = ((Number) l).doubleValue();
        return x*x; // result is auto-boxed to return a Double.
      }
    }

PostfixMathCommand and its subclasses provide a number of convenience methods for converting arguments: `asInt`, `asStrictInt`, `asLong`, `asDouble`, `asString`, `asBool` which return the corresponding primitive type and throw exceptions if necessary. Using the asDouble method the code becomes

    import com.singularsys.jep.functions.UnaryFunction;

    public class Square extends UnaryFunction {
      public Object eval(Object l)  throws EvaluationException  {
        // Convert argument at position 0 to double, throwing exception if needed
        double x = asDouble(0,l);
        return x*x;
      }
    }

Jep 4.1 adds the generic

    protected  T asType(final Class type,int position,Object value)
    	                            throws IllegalParameterException

method, that can be used to convert to any type.

### Nary functions

If the function accepts 0, more than 2 or a variable number of arguments then the NaryFunction base class can be used. Here the arguments are passed as an array of objects `Object eval(Object[] args)`. By default, these functions will accept any number of argument. A fixed number of allowed arguments can be specified in the `public NaryFunction(int nArgs)` constructor. A restricted but variable number of arguments can be specified by overriding the `public boolean checkNumberOfParameters(int n)` method which returns true if n is a valid number of arguments. Both these methods will check the number of arguments at both parse and evaluation time.

For example the following function always takes zero arguments would return the current time.

    import com.singularsys.jep.functions.NaryFunction;

    public class Time extends NaryFunction {
      public Time() {
        super(0); // does not allow any arguments
      }
      public Object eval(Object[] args) {
       return System.currentTimeMillis();
      }
    }

The following calculates the sum of squares allows two or three arguments

    import com.singularsys.jep.functions.NaryFunction;

    public class SumSq extends NaryFunction {
      // Use default constructor: variable number of arguments
      @Override
      public boolean checkNumberOfParameters(int n) {
        return (n == 2 || n == 3);
      }

      public Object eval(Object[] args) throws EvaluationException {
        double x = asDouble(0,args[0]);
        double y = asDouble(1,args[1]);
        if(args.length==2)
          return x*x+y*y;
        else {
          double z=asDouble(2,args[2]);
          return x*x+y*y+z*z;
        }
      }
    }

A more general version allowing one or more arguments would be

    import com.singularsys.jep.functions.NaryFunction;

    public class SumSq extends NaryFunction {
      // Use default constructor: variable number of arguments

      // Ensure at least one argument
      @Override
      public boolean checkNumberOfParameters(int n) {
        return (n == 2 || n == 3);
      }

      public Object eval(Object[] args) throws EvaluationException  {
        double x = asDouble(0,args[0]);
        double res = x*x;
        for(int i=1;i<args.length;++i) {
          double y = asDouble(i,args[i]);
          res += y*y;
        }
        return res;
      }
    }

### Stack based PostfixMathCommand

The old style PostfixMathCommand is still available. These use a stack based evaluation via the `run(Stack<Object> inStack)` method.

1. Create a class that extends PostfixMathCommand (in the `com.singularsys.jep.functions` package). In this example we will name it "Half"
2. In the constructor set the number of arguments to be taken. In our case this is one. Do this by writing `numberOfParameters = 1;`
   If you want to allow any number of parameters, initialize the `numberOfParameters` value to -1. It is recommended to study the `Sum.java` code as an example of a function that accepts any number of parameters.
3. Implement the`run(Stack<Object> inStack)` method. The parameters are passed in a Stack object. This same stack is used as the output for the function. So we first need to pop the parameter off the stack, calculate the result, and finally pop the result back on the stack.

       public void run(Stack<Object> inStack) throws EvaluationException {

          // check the stack
          checkStack(inStack);

          // get the parameter from the stack
          Object param = inStack.pop();

          // check whether the argument is of the right type
          if (param instanceof Double) {
             // calculate the result
             double r = ((Double)param).doubleValue() / 2;
             // push the result on the inStack
             inStack.push(new Double(r));
          } else {
             throw new EvaluationException("Invalid parameter type");
          }
       }

4. In your main program that is using the parser, add the new function. Do this by writing

       jep.getFunctionTable().addFunction("half", new Half());

5. If numberOfParameters == -1 you may wish to overwrite the `boolean checkNumberOfParameters(int n)` to catch an illegal number of arguments at parse time.
6. In Jep version 3.3 a new IllegalParameterException is introduced. This simplifies and standardizes the common case of function arguments of the wrong types. In addition, the PostfixMathCommand class provides methods `String asString()`, `int asInt()`, `int asStrictInt()`, `int asLong()`, `int asDouble()` to ease conversion of an argument to the required type, these methods will all throw IllegalParameterException if the argument is not of the required type. The run method above could be simplified to

       public void run(Stack<Object> inStack) throws EvaluationException {
          checkStack(inStack);

          // get the parameter from the stack
          Object param = inStack.pop();

          // convert param to a double throwing an exception
          // the first argument is the name of the function, and the second is its position
          double val = asDouble("half",1,param);

          double r = val / 2;
          inStack.push(new Double(r));
       }

Source files

- CustFunc.java (a testing class)
- Half.java (the Half function class)

### Specialized functions

A number of other base classes and interfaces are also available:

NaryBinaryFunction
  Associative functions like addition and multiplication which can take multiple arguments but the base implementation is a binary function. Just the `Object eval(Object l,Object r)` needs to be implemented.
NullaryFunction
  A function with no arguments, like `rnd()`.
CallbackEvaluationI
  Rather than the values of the arguments the node tree is passed to the function, individual children can be evaluated by a callback function to the evaluator. This allows lazy evaluation where not all branches need to be evaluated. It also allows names of variables to be found.
LValueI
  An interface for functions which can be used on the left-hand side of an assignment.
ZeroLengthBehaviourI
  Allows functions that take array argument to control how they respond to arrays of zero length. Since Jep 4.1.

### Support for Lambda expressions

Starting with Jep 4.0 the `UnaryFunction`, `BinaryFunction`, `NaryBinaryFunction`, and `NullaryFunction` have static factory methods which allow the creation of pfmc's from lambda functions. There are two style of usage: either using lambda functions with `Object` arguments, or using lambda functions with more specific argument types.

The method UnaryFunction.instanceOf(Function<Object,Object> fun) expects a lambda function taking an `Object` argument and returning an `Object` result. This will typically require an explicit cast to a more specific type.

    UnaryFunction recip = UnaryFunction.instanceOf(
             x -> 1.0 / ((Number) x).doubleValue());

The other methods UnaryFunction.instanceOf(Class<T> type,Function<T,? super Object> fun) uses functions where arguments are cast to an explicit type before passing to the function. For example in

    UnaryFunction neg = UnaryFunction.instanceOf(Integer.class, x -> -x );

the argument is cast to an Integer before evaluation. Other argument types will raise an `IllegalParameterException`. This style allows method references to be easily used

    // Static
    UnaryFunction cubrt = UnaryFunction.instanceOf(Double.class,Math::cbrt);
    // Unbound
    UnaryFunction lower = UnaryFunction.instanceOf(
                               String.class, String::toLowerCase);

The other base PFMC classes have similar methods

- BinaryFunction.instanceOf(BiFunction<Object,Object,Object> fun)
- BinaryFunction.instanceOf(Class<T> class,BiFunction<T,T,Object> fun)
- NaryBinaryFunction.instanceOf(BiFunction<Object,Object,Object> fun)
- NaryBinaryFunction.instanceOf(Class<T> class,BiFunction<T,T,Object>fun )
- NullaryFunction.instanceOf(Supplier<Object> fun)

For example

    // Binary Lambda function using cast from objects
    BinaryFunction sum =  BinaryFunction.instanceOf(
            (x,y) -> ((Number) x).doubleValue() + ((Number) y).doubleValue());
    // Binary function where arguments are of type Number
    BinaryFunction diff =  BinaryFunction.instanceOf(Number.class,
            (x,y) -> x.doubleValue() -  y.doubleValue());
    // Binary function with double arguments and using a method reference
    BinaryFunction hypot =  BinaryFunction.instanceOf(Double.class,Math::hypot);
    // Nullary function using a bound reference
    NullaryFunction rnd = NullaryFunction.instanceOf(
                                 ThreadLocalRandom.current()::nextDouble);

These function can be easily be used with Jep, the following sets Jep to perform most operations using integers. (The `NumberFactory` would also need to be changed to make Jep parse numbers as integers).

    Jep jep = new Jep();
    jep.getOperatorTable().getAdd().setPFMC(
                   BinaryFunction.instanceOf(Integer.class, (x,y)->x+y));
    jep.getOperatorTable().getSubtract().setPFMC(
                   BinaryFunction.instanceOf(Integer.class, (x,y)->x-y));
    jep.getOperatorTable().getUMinus().setPFMC(
                   UnaryFunction.instanceOf(Integer.class, x-> -x));
    jep.getOperatorTable().getMultiply().setPFMC(
                   BinaryFunction.instanceOf(Integer.class, (x,y)->x*y));
    jep.getOperatorTable().getDivide().setPFMC(
                   BinaryFunction.instanceOf(Integer.class, (x,y)->x/y));
    jep.addFunction("sqrt", UnaryFunction.instanceOf(
                   Integer.class, x -> (int) Math.sqrt(x)));
    jep.parse("sqrt(x*x+y*y)");
    jep.setVariable("x", 3);
    jep.setVariable("y", 4);
    Object res = jep.evaluate();
    assertEquals(5,res);

See FunctionFromLambdaTest for some JUnit test.

## Function description

Jep 4.1 allows each `PostfixMathCommand` function to have a short description describing its behaviour accessed via the `getDescription()` method. These are used in the console messages. For most standard functions descriptions are read from the `message.properties` file via the JepMessages class. Other classes have overridden the `getDescription()` and individual instances can set a custom description by calling setDescription(String).

## Overloaded functions

The com.singularsys.jep.misc.overloadedfunctions allows functions and operators to be overloaded so that different implementations of a function can be used depending on the types of the arguments or structure of the tree. For example an single `atan` function could be define to allow both the `atan(x)` and `atan(y,x)` syntax, by using the AlternateFunctions, class to combine a unary and binary function.