Parsing Question

51 views
Skip to first unread message

Marcus Gattinger

unread,
Dec 28, 2011, 12:07:39 PM12/28/11
to Jep Java Users
Hi,

I´m completely new to Jep and started to give Jep 3.4 a try. I have a
question concering how to probably best parse something like this:
sum(this.a1234.a5678)

To understand this formula I give some explanation:
"this" refers to some instance of an object (like the JAVA this
keyword). The instance has a list of attributes, each one with an
unique id. The "." accesses the attribute of id 1234. The value of the
attribute contains a list of other objects (of a different type) and
each of them has an attribute of id 5678. The value of the latter
attribute is a number.

this
+--- attribute with id 1234
+--- object1
| +--- attribute widh id 5678 (value is 10)
+--- object2
| +--- attribute widh id 5678 (value is 20)
+--- object3
+--- attribute widh id 5678 (value is 30)

So the result of the formula should be 60.

What is the best way to parse this formula with Jep?

Kind regards,
Marcus

Richard Morris

unread,
Jan 3, 2012, 5:27:05 AM1/3/12
to jep-...@googlegroups.com
Dear Marcus,

Sorry for the delay in getting back.

You can configure Jep to use different syntax for variables. The
ConfigurableParser is made specially to allow such
changes. The following code sets Jep up to allow variable names with a
dot in them, so "this.a1234.a5678" is a valid variable name.

ConfigurableParser cp = new ConfigurableParser();
cp.addHashComments();
cp.addSlashComments();
cp.addDoubleQuoteStrings();
cp.addWhiteSpace();
cp.addExponentNumbers();
cp.addOperatorTokenMatcher();
cp.addSymbols("(",")","[","]",",");
cp.setImplicitMultiplicationSymbols("(","[");

// Sets it up for identifiers with dots in them.
cp.addTokenMatcher(IdentifierTokenMatcher.dottedIndetifierMatcher());

cp.addSemiColonTerminator();
cp.addWhiteSpaceCommentFilter();
cp.addBracketMatcher("(",")");
cp.addFunctionMatcher("(",")",",");
cp.addListMatcher("[","]",",");
cp.addArrayAccessMatcher("[","]");

// Construct the Jep instance and set the parser
jep = new Jep(cp);

// Remove the dot operator
((OperatorTable2)
jep.getOperatorTable()).removeOperator(jep.getOperatorTable().getDot());
//notify other components of change in operator table
jep.reinitializeComponents();


Jep typically works by having each variable having a particular value
jep.addVariable("this.a1234.a5678",this.a1234.a5678);
would set the value of the Jep variable and
this.a1234.a5678 = jep.getVariableValue("this.a1234.a5678");
would get the value.

This could be a little awkward to set up if you want to deal with a
lot of variables. It would be possible to create a new type of
variable which is automatically linked to a java object. I'd need a
bit of time to think about how that might be done.

Hope thats of help

Richard

Richard Morris

unread,
Jan 3, 2012, 6:37:48 AM1/3/12
to jep-...@googlegroups.com
Another thought, if linking jep variables to java objects is important
you could use the
com.singularsys.jep.misc.VariableTableObserver
which checks for when jep variables are changed and then calls a
method in the observer.

Richard

Marcus Gattinger

unread,
Jan 3, 2012, 1:20:06 PM1/3/12
to Jep Java Users
Hi Richard,

thanks for your reply. Your suggestion confirms what I´ve found out by
the product´s documentation. I´ve concluded to evaluate the variable
values in a separate parsing and evaluation process and set the
variable values to do the evaluation of the complete expression (i. e.
the expression that contains functions and operators). Please let me
know if you have found a way to automatically link variables to java
objects.

By the way, Jep seems to be a great framework!

Kind regards,
Marcus

Richard

unread,
Jan 18, 2012, 11:40:21 AM1/18/12
to Jep Java Users
Marcus,

After a bit of thinking about it I've worked out a way of linking jep
variables to objects it does involve quite a few classes

VariableBinding: an interface which defines how a variable can be
bound to an object
BoundVariable: a variable bound to an object
BoundVariableFactory: a factory for creating bound variables
FieldVariableBinding: A particular VariableBinding which links
variables to a particular field on an object
VariableBindingMapper: An interface defining what to do with new
variable names
ChainedObjectVariableBindingMapper: Translates a variable names of the
form z.a.b into reference to a field of a field of an object.
BoundVariableTest: Test class and example

Richard

************ VariableBinding **************************
package com.singularsys.jep.misc.boundvariable;

import java.io.Serializable;

/**
* Bound variables will call these methods to interact with objects
outside of Jep.
*
* @author Richard Morris
* @since Jep 3.5
*/
public interface VariableBinding extends Serializable {
/**
* Gets the value of a variable.
* @return the value
* @throws Exception if there is a problem getting the variable value
*/
Object getValue() throws Exception;
/**
* Sets the value of a variable.
* @param value
* @throws Exception if there is a problem setting the variable value
*/
void setValue(Object value) throws Exception;
}

*********** BoundVariable ******************
package com.singularsys.jep.misc.boundvariable;

import com.singularsys.jep.Variable;

/**
* A variable bound to an object outside of Jep.
* @author Richard Morris
* @since Jep 3.5
*/
public class BoundVariable extends Variable {
private static final long serialVersionUID = 350L;
protected VariableBinding vb;

/**
* Create a BoundVariable
* @param name name of the variable
* @param vb binding for the variable
*/
public BoundVariable(String name,VariableBinding vb) {
super(name);
this.vb = vb;
}

/**
* Gets the value of the variable.
* @return the value of the object. Returns null if there is problem
accessing the variable.
*/
@Override
public Object getValue() {
try {
return vb.getValue();
} catch (Exception e) {
return null;
}
}

/**
* Sets the value of the variable.
* @return false if there is a problem accessing the variable
*/
@Override
protected boolean setValueRaw(Object object) {
try {
vb.setValue(object);
this.setValidValue(true);
return true;
} catch(Exception e) {
return false;
}
}
}
************** BoundVariableFactory ******************
package com.singularsys.jep.misc.boundvariable;

import com.singularsys.jep.Variable;
import com.singularsys.jep.VariableFactory;

/**
* <p>A variable factory for creating bound variables.</p>
* <p>
* If a variable is called with an {@link VariableBinding} as its
value then
* a {@link BoundVariable} will be created.
* </p>
* @author Richard Morris
* @since Jep 3.5
*/
public class BoundVariableFactory extends VariableFactory {
private static final long serialVersionUID = 350L;
protected VariableBindingMapper vom;

/**
* Create the factory with no VariableMapping.
* When a new variable is requested
*/
public BoundVariableFactory() {
}

/**
* Create the factory with a specific VariableMapping for creation of
new variables.
* @param vom
*/
public BoundVariableFactory(VariableBindingMapper vom) {
super();
this.vom = vom;
}


@Override
public Variable createVariable(String name) {
if(vom!=null) {
VariableBinding obj = vom.mapVariableName(name);
if(obj!=null)
return createVariable(name,obj);
}
return super.createVariable(name);
}


@Override
public Variable createVariable(String name, Object value) {
if(value instanceof VariableBinding)
return createVariable(name,(VariableBinding) value);
return super.createVariable(name, value);
}

/**
* Creates a variable with the given VariableBinding
* @param name name of the variable
* @param vb binding object
* @return a new BoundVariable
*/
public Variable createVariable(String name,VariableBinding vb) {
Variable var = new BoundVariable(name,vb);
return var;
}

/**
* Gets the VariableMapping for the creation of new variables.
*
* @return the mapping used or null if none set.
*/
public VariableBindingMapper getVariableMapping() {
return vom;
}

/**
* Sets the VariableMapping for the creation of new variables.
* @param vm the mapping
*/
public void setVariableMapping(VariableBindingMapper vm) {
this.vom = vm;
}
}
****************FieldVariableBinding ***************
package com.singularsys.jep.misc.boundvariable;

import java.lang.reflect.Field;

/**
* A {@link VariableBinding} which binds variables to a particular
field of an object.
*
* @author Richard Morris
* @since Jep 3.5
*/
public class FieldVariableBinding implements VariableBinding {
private static final long serialVersionUID = 350L;

protected Object obj;
protected String fieldName;
protected Field field;

/**
* Create the binding object with the name of a field.
* @param obj the object
* @param fieldName the name of the field
* @throws SecurityException
* @throws NoSuchFieldException
*/
public FieldVariableBinding(Object obj, String fieldName) throws
SecurityException, NoSuchFieldException {
super();
this.obj = obj;
this.fieldName = fieldName;
Class<? extends Object> class1 = obj.getClass();
field = class1.getField(fieldName);
}

/**
* Create the binding object with the name of a field.
* @param obj the object
* @param field a field object
*/
public FieldVariableBinding(Object obj, Field field) {
super();
this.obj = obj;
this.field = field;
this.fieldName = field.getName();
}


@Override
public Object getValue() throws IllegalArgumentException,
IllegalAccessException {
Object val = field.get(obj);
return val;
}

@Override
public void setValue(Object value) throws IllegalArgumentException,
IllegalAccessException {
field.set(obj, value);
}

}
**************VariableBindingMapper*******************
package com.singularsys.jep.misc.boundvariable;

import java.io.Serializable;

/**
* Defines a method which translates a variable name into a variable
binding.
* Used when new variable names are encountered.
* @author rich
*
*/
public interface VariableBindingMapper extends Serializable {
/**
* Creates a VariableBinding for a given variable name
* @param name name of the variable
* @return a VariableBinding or null if the name cannot be mapped.
*/
public VariableBinding mapVariableName(String name);
}
************** ChainedObjectVariableBindingMapper ********************
package com.singularsys.jep.misc.boundvariable;

import java.util.HashMap;
import java.util.Map;

/**
* Creates VariableBinding objects by translating the variable name
into a chain of object references.
* For example "foo.a.b" might be translated into a reference to field
"b" of the object at field "a" of an object.
* @author Richard Morris
* @since Jep 3.5
*/
public class ChainedObjectVariableBindingMapper implements
VariableBindingMapper {
private static final long serialVersionUID = 350L;

String sep;
Map<String,Object> baseObjects = new HashMap<String,Object>();

public ChainedObjectVariableBindingMapper(String sep) {
super();
this.sep = sep;
}


public Object put(String key, Object value) {
return baseObjects.put(key, value);
}

@Override
public VariableBinding mapVariableName(String name) {
String[] parts = name.split(sep,-1);
if (parts.length < 2) return null;
Object base = baseObjects.get(parts[0]);
int pos=1;
try {
while(pos < parts.length -1) {
base = base.getClass().getField(parts[pos]).get(base);
++pos;
}
return new FieldVariableBinding(base,parts[pos]);

} catch (Exception e) {
return null;
}
}


}
**************** BoundVariableTest *********************
package com.singularsys.jep.misc.boundvariable;

import static org.junit.Assert.*;

import org.junit.Test;

import com.singularsys.jep.Jep;
import com.singularsys.jep.Variable;
import com.singularsys.jep.parser.Node;

public class BoundVariableTest {

class MyObj {
public Double a;
public double b;
public MyObj c;
}

@Test
public void testObjectFieldAccessor() throws Exception {
Jep jep = new Jep(new BoundVariableFactory());
MyObj obj = new MyObj();
obj.a = new Double(5);
obj.b = 6;
FieldVariableBinding accA = new FieldVariableBinding(obj,"a");
FieldVariableBinding accB = new FieldVariableBinding(obj,"b");

Variable v1 = jep.addVariable("Z_a", accA);
assertEquals(new Double(5),v1.getValue());

Variable v2 = jep.addVariable("Z_b", accB);
assertEquals(new Double(6),v2.getValue());

Variable v3 = jep.addVariable("x", 3.0);
assertEquals(new Double(3),v3.getValue());

Node n = jep.parse("x+Z_a+Z_b");
Object val = jep.evaluate(n);
assertEquals(Double.valueOf(14.0),val);

Node n2 = jep.parse("Z_a=x");
Object val2 = jep.evaluate(n2);
assertEquals(Double.valueOf(3.0),val2);
assertEquals(Double.valueOf(3.0),obj.a);

Node n3 = jep.parse("Z_b=x*4");
Object val3 = jep.evaluate(n3);
assertEquals(Double.valueOf(12.0),val3);
assertEquals(12.0,obj.b,0.01);

}

@Test
public void testObjectVariableFactory() throws Exception {
ChainedObjectVariableBindingMapper uvm = new
ChainedObjectVariableBindingMapper("_");
BoundVariableFactory bvf = new BoundVariableFactory(uvm);
Jep jep = new Jep(bvf);

Variable v3 = jep.addVariable("x", 3.0);
assertEquals(new Double(3),v3.getValue());

MyObj obj = new MyObj();
obj.a = new Double(5);
obj.b = 7;

uvm.put("Z", obj);
Node n = jep.parse("x+Z_a");
Object val = jep.evaluate(n);
assertEquals(Double.valueOf(8.0),val);

MyObj obj2 = new MyObj();
obj2.a = new Double(11);
obj2.b = 13.0;
obj.c = obj2;

Node n2 = jep.parse("x+Z_c_b");
Object val2 = jep.evaluate(n2);
assertEquals(Double.valueOf(16.0),val2);

}
}


On Jan 3, 6:20 pm, Marcus Gattinger <gattinger1...@googlemail.com>
wrote:
Reply all
Reply to author
Forward
0 new messages