All common arithmetic operators are supported, these are provided by the
StandardOperatorTable.
Boolean operators are also fully supported. Relational and logical operators
(>, <, >=, <=, ==, !=, !) are evaluated as Boolean types either
as true
or false
.
An indicates that the operator can be used with the specific type of variable.
Double | Complex | String | Array | ||
Power | ^ | ||||
Boolean Not | ! | ||||
Unary Plus, Unary Minus | +x, -x | ||||
Dot product, cross product | ., ^^ | ||||
Modulus | % | ||||
Division | / | ||||
Multiplication | * | ||||
Addition, Subtraction | +, - | (only +) | |||
Less or Equal, More or Equal | <=, >= | ||||
Less Than, Greater Than | <, > | ||||
Not Equal, Equal | !=, == | ||||
Boolean And | && | ||||
Boolean Or | || | ||||
Assignment | = |
The supplied configurable parser makes the adding operators quite simple by simply having to add new operators to the OperatorTable used by the Jep instance.
Before attempting to add an operator you should familiarize yourself with adding a custom function to Jep.Net. As with custom functions, adding an operator requires an instance of a PostfixMathCommand (pfmc), in order to control how the operator is evaluated (see Adding a custom function).
If you just wish to change the underlying IPostfixMathCommand of an operator, for example to allow it
to work with a new type of value and keeping the existing precedence and associativity and symbol,
then the SetPFMC()
method of Operator can be called. The operator can be
recovered from the various get methods of the OperatorTable, for instance to change the
IPostfixMathCommand of the And (&&) operator use
jep.OpTab.GetAnd().SetPFMC(new LazyLogical(LazyLogical.AND));
Operators can either be binary operators (a+b), prefix unary operators (!a),
or suffix unary operators (i.e. like the increment operator x++, although no suffix operators
are defined as standard). Static fields defining flags
for each of these are defined in the Operator
class, these are Operator.BINARY
, Operator.UNARY
,
Operator.PREFIX
, Operator.SUFFIX
.
The order in which operators are parsed depends on their precedence. For instance 1+2*3 is interpreted as 1+(2*3). The * operator has precedence over + (or in other words, it is "more tightly bound"). In Jep.Net we use the convention that low values for precedence give a tighter binding, so the numeric value of the precedence of * is less than that of +. Precedences are always set in a relative manner as discussed below.
The parsing of operators also depends on their binding or associativity.
This can either be left-associative (use Operator.LEFT)
, like
"=" where a=b=c is interpreted as a=(b=c), or right-associative (Operator.RIGHT
),
like "-" where a-b-c is interpreted as (a-b)-c. Left or right should be specified
for all binary operators. A third flag Operator.ASSOCIATIVE
is
used to indicate associative operators like "+" this is used by print routine
to determine whether to include brackets or not.
To construct an operator the constructor public Operator(String name,IPostfixMathCommand
pfmc,int flags)
is used, giving the symbol used for the operator, the
IPostfixMathCommand used and the sum of the flags. A second constructor
public Operator(String name,String symbol,IPostfixMathCommand pfmc,int flags)
is used when two operators share the same symbol, for example
Operator add = new Operator("+",new Add(), Operator.BINARY+Operator.LEFT+Operator.ASSOCIATIVE); Operator sub = new Operator("-",new Subtract(), Operator.BINARY+Operator.LEFT); Operator mul = new Operator("*",new Multiply(), Operator.BINARY+Operator.LEFT+Operator.ASSOCIATIVE); Operator div = new Operator("/",new Add(), Operator.BINARY+Operator.LEFT); Operator umin = new Operator("UMinus","-",new UMinus(), Operator.UNARY+Operator.RIGHT+Operator.PREFIX); Operator equals = new Operator("=",new Assign(), Operator.BINARY+Operator.RIGHT);
There are further flags available, these specify other mathematical properties of the operators which are not currently used.
Once the new operators are created they need to be added to the operator table there are four methods for doing this.
public Operator AddOperator(int key,Operator op); public Operator AddOperator(int key,Operator op,Operator existingOp); public Operator InsertOperator(int key,Operator op,Operator existingOp); public Operator AppendOperator(int key,Operator op,Operator existingOp);
The first adds an operator with no precedence set, the second adds an operator with the same precedence as an existing operator, the third and forth create a new precedence level which is either before (i.e. tighter) or after (i.e. looser) than the existing operator. The first argument in each is a numeric value used to represent the operator, for new operators use
OperatorTable ot = jep.OpTab; ot.AddOperator(ot.NumOps, new Operator("~", new Round(), Operator.UNARY + Operator.PREFIX));If you are replacing one of the standard operator use the values of one of the static fields this will mean that the corresponding Get...() methods will make the operator available
ot.AddOperator(OperatorTable.OP_AND, new Operator("AND",
new Logical(Logical.AND), Operator.BINARY +
Operator.LEFT + Operator.ASSOCIATIVE));
ot.GetAnd(); // returns the AND operator.
An alternative way of setting operator precedence is the SetPrecedenceTable method which sets the precedence of all operators at once.
Once the operators have been added then the jep.ReinitializeComponents();
to update jep instance with the new operators.
The following is example output of the OperatorTable.ToString()
method.
Operator: "^-1" UDivide unary, Prefix, right binding, precedence -1. Operator: "LIST" variable number of arguments, infix, right binding, precedence -1. Operator: "[]" variable number of arguments, infix, right binding, precedence -1. Operator: "^" binary, infix, right binding, precedence 0. Operator: "-" UMinus unary, Prefix, right binding, precedence 1. Operator: "+" UPlus unary, Prefix, right binding, precedence 1. Operator: "!" unary, Prefix, right binding, precedence 1. Operator: "*" binary, infix, left binding, associative, commutative, precedence 2. Operator: "/" binary, infix, left binding, precedence 2. Operator: "%" binary, infix, left binding, precedence 2. Operator: "." binary, infix, left binding, precedence 2. Operator: "^^" binary, infix, left binding, precedence 2. Operator: "+" binary, infix, left binding, associative, commutative, precedence 3. Operator: "-" binary, infix, left binding, precedence 3. Operator: ">" binary, infix, left binding, precedence 4, transitive. Operator: "<" binary, infix, left binding, precedence 4, transitive. Operator: "<=" binary, infix, left binding, precedence 4, reflexive, transitive. Operator: ">=" binary, infix, left binding, precedence 4, reflexive, transitive. Operator: "==" binary, infix, left binding, precedence 5, equivalence relation. Operator: "!=" binary, infix, left binding, precedence 5, symmetric. Operator: "&&" binary, infix, left binding, associative, commutative, precedence 6. Operator: "||" binary, infix, left binding, associative, commutative, precedence 7. Operator: "=" binary, infix, right binding, precedence 8.
Once the operators have been added then the jep.ReinitializeComponents();
method must be called to inform the parser about the new operators. Once this
is done the parser will be able to use the new operators. It is important
that the precedence and associatitivity of the operators has been set correctly.