Operators for logical or

63 views
Skip to first unread message

Marcus Gattinger

unread,
Jan 5, 2012, 4:06:05 PM1/5/12
to Jep Java Users
Hi,

I try to add some alternative expressions for evaluating a logical or
and I want to support all of the following expression forms:
or(x > 1, x < -1)
x > 1 || x < -1
x > 1 | x < -1
x > 1 or x < -1

I was able to get the last three expression forms up and running. But
I don´t know how to do this for the first one. Here is the code, I
have tried:

First the code of my function class "Or":
import com.singularsys.jep.EvaluationException;
import com.singularsys.jep.functions.NaryFunction;

public class Or extends NaryFunction {
@Override
public boolean checkNumberOfParameters(int n) {
return n > 0;
}

@Override
public Object eval(Object[] args) throws
EvaluationException {
boolean result = asBool(0, args[0], true);
if (result == false) {
for (int i = 1; i < args.length; i++) {
result = result | asBool(i, args[i], true);
if (result == true) break;
}
}
return result;
}
}

Next the setup of the Jep parser:

ConfigurableParser cp = new ConfigurableParser();
cp.addHashComments();
cp.addSlashComments();
cp.addDoubleQuoteStrings();
cp.addSingleQuoteStrings();
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 jep = new Jep(cp);

// Access the operator table to make some modifications to the
default settings.
OperatorTable2 ot = (OperatorTable2)jep.getOperatorTable();

// Add alternatives of the logical or operator:
// - Get the default or operator (it uses the symbol "||").
Operator defaultOr = ot.getOr();
// - Based on the default operator, create new ones but use
the symbols "|" and "or".
Operator newOr1 = new Operator("|", defaultOr.getPFMC(),
defaultOr.getFlags());
Operator newOr2 = new Operator("or", defaultOr.getPFMC(),
defaultOr.getFlags());
// - Add the new operators with the same precedence level as
the default operator.
ot.addOperator(new EmptyOperatorTable.OperatorKey() {},
newOr1, defaultOr);
ot.addOperator(new EmptyOperatorTable.OperatorKey() {},
newOr2, defaultOr);

// Add logical functions as alternatives to the default
operators.
jep.getFunctionTable().addFunction("or", new Or());

// Notify other components of change in operator and function
table.
jep.reinitializeComponents();

// Test it
jep.addVariable("x", 2);
List<String> formulas = new ArrayList<String>();
formulas.add("or(x > 1, x < -1)");
formulas.add("x > 1 || x < -1");
formulas.add("x > 1 | x < -1");
formulas.add("x > 1 or x < -1");
for (String formula : formulas) {
try {
jep.parse(formula);
Object result = jep.evaluate();
System.out.println(String.format("%s = %s", formula,
result));
} catch (Exception ex) {
System.out.println(String.format("%s throws exception
of type %s: %s", formula, ex.getClass().getName(), ex.getMessage()));
}
}

This is the exception I get for the first expression:
com.singularsys.jep.configurableparser.matchers.GrammarException:
Unexpected token found "or"

I also looked at the postings "Operators that are strings" and "AND
operator not recognized" but the code there seems not to work for me
(don´t know why).

So could anyone help please?

Regards, Marcus

Richard

unread,
Jan 6, 2012, 6:39:58 AM1/6/12
to Jep Java Users
Marcus,

This one is tricky. "or" is being used in two different fashions as a
binary operator or as a prefix function. By default the parser checks
for operators first then functions so when parsing "or(x > 1, x < -1)"
it finds the binary operator "or" rather than the function. Its almost
an ambiguous grammar as
"(x > 1) or (x < -1)" could either be treated as a binary operator or
as (x>1) times the function "or(x < -1)".

There are ways to get round this, but they are a bit hacky. However
I'd recommend changing the grammar so only one form is allowed.

If you do want to allow both, the easiest way might be to use a custom
GrammarMatcher possibly modifying
com.singularsys.jep.configurableparser.matchers.FunctionGrammarMatcher

The code for that would be the same apart from the start of the match
method
public Node match(Lookahead2Iterator<Token> it, GrammarParser
parser) throws ParseException {
Token t = it.peekNext();
if(t==null) return null;

// code to check if the first token matches our function name
boolean matchSpecFunName = t.getSource().equals("or");
if(!t.isFunction() && !matchSpecFunName) return null;
String name = t.getSource();

// get the corresponding PFMC
PostfixMathCommandI pfmc = matchSpecFunName ? ((OperatorToken)
t).getBinaryOp().getPFMC() : ((FunctionToken) t).getPfmc();

the rest of the method looks for a sequence like ( ... , ... )

If you create your own GrammarMatcher you can add it to the parser
using cp.addGrammarMatcher(gm);

Richard

On Jan 5, 9:06 pm, Marcus Gattinger <gattinger1...@googlemail.com>
wrote:

Richard Morris

unread,
Jan 11, 2012, 12:38:14 PM1/11/12
to Jep Java Users
Further to the above the following class adds facilities to treat
operators as functions

package com.singularsys.jep.misc;


import java.util.ArrayList;
import java.util.List;

import com.singularsys.jep.Jep;
import com.singularsys.jep.JepMessages;
import com.singularsys.jep.NodeFactory;
import com.singularsys.jep.Operator;
import com.singularsys.jep.ParseException;
import com.singularsys.jep.PostfixMathCommandI;
import com.singularsys.jep.configurableparser.GrammarParser;
import com.singularsys.jep.configurableparser.Lookahead2Iterator;
import com.singularsys.jep.configurableparser.matchers.GrammarException;
import com.singularsys.jep.configurableparser.matchers.GrammarMatcher;
import com.singularsys.jep.configurableparser.tokens.OperatorToken;
import com.singularsys.jep.configurableparser.tokens.TerminatorToken;
import com.singularsys.jep.configurableparser.tokens.Token;
import com.singularsys.jep.parser.Node;

/**
* A GrammarMatcher which matches functions in the form '+(x,y)' where
+ is an operator.
*/
public class OperatorAsFunctionGrammarMatcher implements GrammarMatcher {
private static final long serialVersionUID = 300L;
/**
* @serial
*/
private final Token open; // = new SymbolToken("(");
/**
* @serial
*/
private final Token close; // = new SymbolToken(")");
/**
* @serial
*/
private final Token comma; // = ",";

/**
* @serial
*/
private List<Operator> operators;

private transient NodeFactory nf;

/**
* Create a OperatorAsFunctionGrammarMatcher
* @param open token representing an opening bracket
* @param close token representing a closing bracket
* @param comma token representing a list item separator
* @param operators list of operators which will be matched
*/

public OperatorAsFunctionGrammarMatcher(Token open, Token close,
Token comma, List<Operator> operators) {
this.open = open;
this.close = close;
this.comma = comma;
this.operators = operators;
}

public void init(Jep jep) {
nf = jep.getNodeFactory();
}

/**
* Generate a token to use in GrammarExecptions
* @param it used to get current position
* @return a TerminatorToken
*/
protected Token errorToken(Lookahead2Iterator<Token> it) {
Token t2 = it.peekNext();
if(t2==null) {
Token t3 = it.prev();
t2 = new TerminatorToken(JepMessages.getString("configurableparser.matchers.FunctionGrammarMatcher.TerminatorTokenName"));
//$NON-NLS-1$
t2.setPosition(t3.getLineNumber(), t3.getColumnNumber());
}
return t2;
}
/**
* Attempt to match a function, calls the
GrammarParser.parseSubExpression()
* to match function arguments.
*/


public Node match(Lookahead2Iterator<Token> it, GrammarParser
parser) throws ParseException {
Token t = it.peekNext();
if(t==null) return null;

if(!t.isOperator()) return null;
OperatorToken ot = (OperatorToken) t;
Operator matchedOp=null;
for(Operator op:operators) {
if(ot.getBinaryOp()==op || ot.getPrefixOp()==op ||
ot.getSuffixOp()==op)
matchedOp=op;
}
if(matchedOp==null) return null;
String name = t.getSource();
PostfixMathCommandI pfmc = matchedOp.getPFMC();
if(!open.equals(it.nextnext())) return null;
it.consume();
it.consume();
if(close.equals(it.peekNext())) {
if(!pfmc.checkNumberOfParameters(0)) {
int req = pfmc.getNumberOfParameters();
if(req>=0)
throw new
GrammarException(String.format(JepMessages.getString(
"FunctionRequiresNArgumentsFoundN"), //$NON-NLS-1$
pfmc.getName(),req,0),errorToken(it));

throw new GrammarException(String.format(JepMessages.getString(

"FunctionIllegalNumberOfArguments"),pfmc.getName(),0),errorToken(it));
//$NON-NLS-1$
}
it.consume();
return nf.buildFunctionNode(name, pfmc,new Node[0]);
}
List<Node> seq=new ArrayList<Node>();
while(true) {
Node contents = parser.parseSubExpression();
seq.add(contents);
if(close.equals(it.peekNext())) // this way round to cope with null
break;
else if(comma.equals(it.peekNext()))
it.consume();
else
throw new
GrammarException(String.format(JepMessages.getString("configurableparser.matchers.FunctionGrammarMatcher.Function"),pfmc.getName()),close,errorToken(it));
//$NON-NLS-1$
}
it.consume();
if(!pfmc.checkNumberOfParameters(seq.size())) {
int req = pfmc.getNumberOfParameters();
if(req>=0)
throw new GrammarException(String.format(JepMessages.getString(
"FunctionRequiresNArgumentsFoundN"), //$NON-NLS-1$
pfmc.getName(),req,seq.size()),errorToken(it));

throw new GrammarException(String.format(JepMessages.getString(
"FunctionIllegalNumberOfArguments"), //$NON-NLS-1$
pfmc.getName(),seq.size()),
it.prev());
}
return nf.buildFunctionNode(name, pfmc,seq.toArray(new
Node[seq.size()]));
}


}


It allows selected operators to be treated as functions, and can be
added to the parse as

cp.addGrammarMatcher(new OperatorAsFunctionGrammarMatcher(
cp.getSymbolToken("("),
cp.getSymbolToken(")"),
cp.getSymbolToken(","),
Arrays.asList(newOr2) // list of operators
));

A full example would be

@Test
public void testOperatorsAsFunctions() throws ParseException, Exception {

ot.addOperator(new EmptyOperatorTable.OperatorKey() {},
newOr1, defaultOr);

Operator newOr2 = new Operator("or", defaultOr.getPFMC(),
defaultOr.getFlags());


ot.addOperator(new EmptyOperatorTable.OperatorKey() {},
newOr2, defaultOr);

cp.addGrammarMatcher(new OperatorAsFunctionGrammarMatcher(
cp.getSymbolToken("("),
cp.getSymbolToken(")"),
cp.getSymbolToken(","),Arrays.asList(newOr2 )));

// Add logical functions as alternatives to the default operators.

//jep.getFunctionTable().addFunction("or", new Or());


// Notify other components of change in operator and function table.
jep.reinitializeComponents();

// Test it
jep.addVariable("x", 2);
List<String> formulas = new ArrayList<String>();

//formulas.add("sin(x)");


formulas.add("x > 1 or x < -1");
formulas.add("or(x > 1, x < -1)");
formulas.add("x > 1 || x < -1");
formulas.add("x > 1 | x < -1");

for (String formula : formulas) {
Node n = jep.parse(formula);
Object result = jep.evaluate(n);
System.out.println(String.format("\"%s\" = \"%s\" =
%s", formula, jep.toString(n), result));
}
}

Richard

> --
> You received this message because you are subscribed to the Google Groups "Jep Java Users" group.
> To post to this group, send email to jep-...@googlegroups.com.
> To unsubscribe from this group, send email to jep-users+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/jep-users?hl=en.
>

Reply all
Reply to author
Forward
0 new messages