- JavaDoc

# JEP

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

# Advanced Features

- Manipulating Expressions
- Components
- Configurable Parser
- Serialization
- Grammar

# 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.

A number of different base classes are available:

UnaryFunction
  Functions that take a single argument, the `Object eval(Object l)` must be implemented.
BinaryFunction
  Functions taking two arguments, defines a `Object eval(Object l,Object r)` method.
NaryFunction
  Functions taking an arbitrary number of arguments, defines a `Object eval(Object[] args)` method.
PostfixMathCommand
  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;
      }
    }

### 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 highly 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 or applet 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 standadises 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)

### Specialised functions

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

NaryBinaryFunction
  Functions like addition and mulitplication 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.
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.