New Variable Types and Arrays

40 views
Skip to first unread message

rld...@gmail.com

unread,
Nov 25, 2015, 2:46:06 PM11/25/15
to Jep Java Users
I have been looking at JEP for a couple of days and am wondering how I would go about some actions. I am currently looking at the 2.4.1 extended JEP for a test script language. Hopefully the initial information I give will make by questions clearer in what I am trying to do.

Basic questions are:
  - single symbolTable versus multiple
  - get variable type stored in a symbol table
  - how to create / manipulate arrays that are the basic types

These questions are expanded out better after the following description of what I am trying to do.


First, I am setting up a test script that will allow things like

define:int          i=33
define:string sVar = "userid1"
define:double dVar = 235.12
define:boolean bFlag = false
define:int[] avar=[10, 11, 12, 13, 14]
define:int iVar2 = aVar[3] (I like zero based so this would be 13)
define:string[] auserids = ["userid1", "userid2"]



for double, boolean, and string, I set up in my code:
JEP jep = newJEP();
jep.addStandardFunctions();
jep.addStandardConstants();
jep.setImplicitMul(false);
jep.setAllowUndeclared(true);
jep.setAllowAssignment(true);

SymbolTable  hSymbol = jep.getSymbolTable();


In order to use Integers, I added the following statements
JEP jepInt = new GroupJep(new Integers());
jepInt = new GroupJep(new Integers());
jepInt.addStandardFunctions();
jepInt.addStandardConstants();
jepInt.setImplicitMul(false);
jepInt.setAllowUndeclared(true);
jepInt.setAllowAssignment(true);
SymbolTable  hSymbolGroup = jepInt.getSymbolTable();




I do not add the complex constants so that a variable named "i" can be created by whoever wants to define on. This is not really being used for pure mathematical operations.

I then parse a CSV file for a line like  "define:int  i=13".  The user wants an integer to be defined (like java Integer i = 13;). When I see a line like this, I call a method defineVariable("i=13", true). The method is shown below


The call to this does define the element and stores it as an Integer. for the define:double, define:string, and define:boolean declarations, I use the jep value versus the jepInt value.  Call is like:
// see if we want an Integer. If so, set the flag
if (sType.equals("int")) bInteger = true;
// Parse and store the value
sKey = defineVariable(sExpression, bInteger);

=====================================


/**
* Parse the passed in expression creating the variable in the SymbolTable. This method is intended
* to not only parse the passed in expression and save its value for later, but to also return to
* the caller the name of the symbol that has been defined.
* @param sExpression expression to parse to add a new variable
* @param bInt true, value is an integer, false, value is a double, string, boolean
* @return String variable name added. If none assigned, returns a null
*
*/
protected String defineVariable(String sExpression, Boolean bInt) {
String sKey = null;
JEP jVar = jep; // default to handle double, string, boolean
try {
// If Integers are needed, point to the JEP group defined for Integers.
if (bInt) {
jVar = jepInt;
}

// In parsing and executing the expression, we do not need to get the value
// returned from the evaluate because we are just interested in storing it for later.
Node node = jVar.parse(sExpression);
jVar.evaluate(node);
// IS THERE A BETTER WAY TO GET THE VARIABLE NAME?  SHOULD I CHECK THERE IS AN
// ASSIGNMENT TO USE THIS?



// Now get some information from the node This node needs to be an assignment
// operation so the fnction would be "=". If it is not an assignment, no way
// we could define the variable. The left hand side (lhs) of the expression is
// the variable name.
Node lhsNode = node.jjtGetChild(0);
if(lhsNode instanceof ASTVarNode)
{
ASTVarNode vn = (ASTVarNode) lhsNode;
sKey = vn.getName();
} else if(lhsNode instanceof ASTFunNode && ((ASTFunNode) lhsNode).getPFMC() instanceof LValueI) {
sKey = ((LValueI) ((ASTFunNode) lhsNode).getPFMC()).toString();
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return sKey;

}


The above works for Integer, String, Boolean, and Double. Input data looks ike:

tc:start 11:12:13
define:int j=21
define:int i=j-13
define:string sValue="the string"
define:double dbl=12.6
define:boolean bFlag=false


Output from this simple run gives:

framework.Execute_Functional_Test:139  Running testcase: var_test
        actions.tc.Start:55  Testcase start will occur at index: 1

framework.Parse_Functional_Test:129  Running test {var_test) with loop parameters: 11:12:13    SIDE note, want to create an array with these three items
framework.Parse_Functional_Test:166  [4] define:int j=21
        actions.stmts.Define:106  variable declared: int j = 21

framework.Parse_Functional_Test:166  [5] define:int i=j-13
        actions.stmts.Define:106  variable declared: int i = 8

framework.Parse_Functional_Test:166  [6] define:string sValue="the string"
        actions.stmts.Define:106  variable declared: string sValue = the string

framework.Parse_Functional_Test:166  [7] define:double dbl=12.6
        actions.stmts.Define:106  variable declared: double dbl = 12.6

framework.Parse_Functional_Test:166  [8] define:boolean bFlag=false
        actions.stmts.Define:106  variable declared: boolean bFlag = false


=======================================
NOW FOR THE QUESTIONS

  1. Is there a simple way that I could just have one JEP variable defined that will store in a single symbol table the values for Integer, Double, String, and Boolean?  Right now I have to have an external map that defines the variable type that was declared and get it from either jep or jepInt.  As can be seen, I am not planning on really using this for complex mathematical operations although I do not know what a user may put in the defines.
  2. Instead of storing the resulting variable name and type in another HashMap, is there someplace I can get this information from what has been created? I guess part of this question is can I also just determine by using the SymbolTable if it is there so I do not need to have the external HashMap?  I guess I could do something like:

if (hSymbolGroup.contains(key)) {
// it is an integer and defined
} else if (hSymbol.contains(key)) {
// it is either a double, boolean, or string - how to determine?
// this is the problem area.
} else {
// variable not defined yet
}
Of course, if this is all in a single symbolTable, then the Integer part is also a problem to know what the variable type is.

Side note, these declarations will be used to control for, while, forach, switch, and if operations. They will also be used to pass information to other commands - not defined yet but will control testing operations on a browser.

==========

Now, for the more difficult question to me. How do I go about creating and working with arrays?  Right now, I am not able to understand The documentation enough to build arrays. I have seen documenation about vectors but it is not clear enough at my knowledge level to determine quite what to do. I want to be able to define and use arrays:

define:int iLen = args.length()
define:int iArg1=args[1]
define:int iArg2=args[2]
define:int iArg3=args[3]
define:string[] auserids = ["userid1", "userid2", "userid3" ] // don't really need the "'s unless a comman in the string

this would be used in a foreach loop like
sys:foreach   string a  :  aUserids

My code in the foreach would get the length of the array and loop on each element of the array 
something like  String sName = (String) getElement(auserids, index).  

============
Any assistance would be appreciated.
Thank you.

Robert



Richard Morris

unread,
Nov 27, 2015, 1:14:40 PM11/27/15
to Jep Java Users
Dear Robert,

It looks like you have got quite deeply into JEP.

For you first question about using different types of variables. The type system in JEP does tend to assume most things are doubles. I am quite close to releasing a new set of extensions for the Jep 3.4 line with an improved type system with precise control over the way different variable types interact.

I would generally stear clear of using GroupJep, it was devised for quite specific purposed of representing abstract mathematical groups. You only have access to a limited range of operations +, -, *, / , mod and pow and almost no functions. You won't be able to convert between integers and other datatypes.

The way I would tackle your problem is to define new PFMC's for each operator

package test;

import org.nfunk.jep.ParseException;
import org.nfunk.jep.function.Add;

public class StrictAdd extends Add {

   
public Object add(Object d1, Object d2) throws ParseException {
       
if(d1 instanceof Integer && d2 instanceof Integer) {
           
Integer I1 = (Integer) d1;
           
Integer I2 = (Integer) d2;
           
int i3 = I1.intValue() + I2.intValue();
           
return Integer.valueOf(i3);
       
}
       
return super.add(d1, d2);
   
}
}


You would also need to define a new NumberFactory type so that literal numbers are converted to the correct type

package test;

import org.nfunk.jep.ParseException;
import org.nfunk.jep.type.Complex;
import org.nfunk.jep.type.NumberFactory;

public class StrictNumberFactory implements NumberFactory {

    /**
     * This is the only important method. It defines what to do with literal numbers
     * encountered during parsing. If the input text is an integer like "3" it will return an integer,
     * otherwise it will return a double, i.e. for an input like "3.14". 
     */
    public Object createNumber(String value) throws ParseException {
       double d = Double.parseDouble(value);
       int i = (int) d;
       if( i == d) 
           return Integer.valueOf(i);
        
        return Double.valueOf(d);
    }

    public Object createNumber(double value) throws ParseException {
        return Double.valueOf(value);
    }

    public Object createNumber(int value) throws ParseException {
        return Integer.valueOf(value);
    }

    public Object createNumber(short value) throws ParseException {
        return Integer.valueOf(value);
    }

    public Object createNumber(float value) throws ParseException {
        return Double.valueOf(value);
    }

    public Object createNumber(boolean value) throws ParseException {
        return Boolean.valueOf(value);
    }

    // Not actually used so can just use null
    public Object createNumber(Number value) throws ParseException {
        return null;
    }

    // Not actually used so can just use null
    public Object createNumber(Complex value) throws ParseException {
        return null;
    }

    public Object getZero() {
        return Integer.valueOf(0);
    }

    public Object getOne() {
        return Integer.valueOf(1);
    }

    public Object getMinusOne() {
        return Integer.valueOf(-1);
    }

    public Object getTwo() {
        return Integer.valueOf(2);
    }
}



You could then tie this all together using 

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        JEP jep = new JEP(false,true,false,new StrictNumberFactory());

        jep.addStandardFunctions();

        jep.addStandardConstants();         

        jep.setImplicitMul(false);

        jep.setAllowUndeclared(true);

        jep.setAllowAssignment(true);


        // Now use our custom type

        jep.getOperatorSet().getAdd().setPFMC(new StrictAdd());

        

        jep.addVariable("i", new Integer(5));

        jep.addVariable("j", new Integer(7));

        jep.addVariable("x", new Double(9.3));

        jep.addVariable("y", new Double(11.3));

        

        Node n;

        try {

            n = jep.parse("i+j");

            Object res = jep.evaluate(n);

            System.out.println("Result "+res+" type "+res.getClass().getSimpleName());


            n = jep.parse("i+3");

            res = jep.evaluate(n);

            System.out.println("Result "+res+" type "+res.getClass().getSimpleName());

        

            n = jep.parse("i+x");

            res = jep.evaluate(n);

            System.out.println("Result "+res+" type "+res.getClass().getSimpleName());


            n = jep.parse("i+3.14");

            res = jep.evaluate(n);

            System.out.println("Result "+res+" type "+res.getClass().getSimpleName());


        } catch (ParseException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }



Note the use of Jep.addVariable(String name, Object val) to create variables of the right type.


I'm away this weekend so I might be delayed in getting back to answer the rest of the question.

Rich

Robert Ducharme

unread,
Nov 28, 2015, 1:29:35 PM11/28/15
to Jep Java Users
Thanks for the initial reply. It helps quite a bit in understanding how things are tied together.
Have a good weekend.

Robert

Richard Morris

unread,
Dec 1, 2015, 10:00:12 AM12/1/15
to Jep Java Users
For the second part about Arrays there are two different ways depending on what you need. 

In regular Jep you can just use the Java's Vectors type and use that as a type for a Jep variable.

Vector v = new Vector();

v.add(new Double(2));

v.add(new Double(3));

v.add(new Double(4));

jep.addVariable("v",v);


You could also add them using the [1,2,3] syntax

Node n = jep.parse("w = [3,2,1]");


To access elements of the array you can use square brackets

n = jep.parse("v[3]");


and set an element using

n = jep.parse("v[1]=1");



This methods is limited and its not intended for doing arithmetic. You cannot add two vectors without some modification.

If you want to treat arrays like mathematical vectors then its better to use the VectorJep package as discussed in Jep 2.4/doc/html/extensions/vectorjep.html.

Richard

Reply all
Reply to author
Forward
0 new messages