Here is a quick discussion on the issue. I will get a working example together (maybe using CHIP) soon. But in the interest of getting you some info quickly, I'll lay out how this is done given the source you have already.
First, look at how operators are added to DTRules in the source. If you go to the class in the dtrules-engine project:
- package com.dtrules.interpreter.operators.RMath
You will see the following:
public class RMath {
static {
new Add(); new Mul(); new Sub(); new Div();
new FAdd(); new FMul(); new FSub(); new FDiv();
new Abs(); new Negate();
new FAbs(); new FNegate();
new Roundto();
}
The point here is that each operator is stateless. The construction of each operator adds it to the primitives dictionary, and makes it available to the decision tables. These operators are what the compiler uses to implement the functionality of the syntax. In other words, EL generates postfix code (made up of these operators) and adds this postfix to the XML of the rule set. DTRules then uses this to execute the tables. This is why you do not have to deploy a compiler (like EL) in your applications.
Anyway, if you look at one of these operators, you can see how they are constructed:
* Absolute value of an integer
public static class Abs extends ROperator {
public void execute(DTState state)throws RulesException {
RInteger.getRIntegerValue(
Math.abs(state.datapop().intValue())
The easiest thing to do is have your code extend ROperator (as shown above). You should then call the super constructor with the name of your function. This name cannot conflict with any of the operator names in the system. The name can be made up of any symbols at all other than white space. This example comes from the 5.0-SNAPSHOT, and going forward you must implement at minimum execute(). You can use the state variable to pull values from the data stack, and push your results to the data stack. state actually allows you to look at any values in the Rules Engine for use by your function.
This is all you have to do. If your function takes a zipcode as a number and returns the state as a string, you can you EL to call your function:
Set the State = the string value of (78750)
Or you can modify EL to create a more usable syntax, maybe something like:
Set the State = the state for the zipcode 78750
You can do this by cloning the el project, and modifying the parser.cup and scanner.flex files.
First add STATEFORZIPCODE variable to parser.cup on line 115.
Add an implementation for STATEFORZIPCODE for converting things to strings on line 811, something like this (I am including a bit of bold. This is at the end of strexpr, so you remove the ';' for the previous option, and replace it with an '|' for your option, then your code ends with the ';'. Again, only consider the bold blue):
RELATIONSHIP_BETWEEN eexpr:e1 AND eexpr:e2 {: RESULT = e1+e2+
"{ type } { '' } "+
"{ over source req { pop dup target req } over if } "+
"relationships forfirstelse swap pop swap pop "; :}
|
STATEFORZIPCODE iexpr:i {: RESULT = i+"stateForZipcode "; :}
;
Then in scanner.sh, add (maybe around line 259) the following code (I am again including a bit of context; you are adding the bold):
"map" {return build(sym.MAP); }
"mapping"{ws}+"key" {return build(sym.MAPPINGKEY); }
"through" {return build(sym.THROUGH); }
/** Our Zip Code tokens **/
"state"{ws}+"for"+{ws}+("a"|"an"|"the")+{ws}*+"zipcode"
{return build(sym.STATEFORZIPCODE); }
Then you need to use a bash shell to execute parser.sh and scanner.sh. Refresh and recompile. Mysgit comes with a bash shell, if you are using windows.
I hope this helps.