I've implemented a mathematical parser in java which can handle ternary operators. The heart of this is the expression method. The input is contained in an iterator it with a it.peekNext() method to view the next token and it.consume() move to the next token. It calls the prefixSuffix() to read constants and variables with possible prefix and suffix operators eg ++x.
protected void expression() throws ParseException {
prefixSuffix();
Token t = it.peekNext();
while(t!=null) {
if(t.isBinary()) {
OperatorToken ot = (OperatorToken)t;
Operator op = ot.getBinaryOp();
pushOp(op,ot);
it.consume();
prefixSuffix();
}
else if(t.isTernary()){
OperatorToken ot =(OperatorToken)t;
Operator to = ot.getTernaryOp();
pushOp(to,ot);
it.consume();
prefixSuffix();
}
else
break;
t=it.peekNext();
}
// read all remaining elements off the stack
while(!sentinel.equals(ops.peek())) {
popOp();
}
}
So when either of the tokens is encountered it calls the pushOp methods to push them on the stack. Each token has an associated operator which is also parsed to pushOp.
pushOp compare the new operator with the top of the stack, poping if necessary
protected void pushOp(Operator op,Token tok) throws ParseException
{
while(compareOps(ops.peek(),op))
popOp();
ops.push(op);
}
The logic for dealing with tenary operators happen in compareOps:
/**
* Compare operators based on their precedence and associativity.
* @param op1
* @param op2
* @return true if op1 has a lower precedence than op2, or equal precedence and a left assoc op, etc.
*/
protected boolean compareOps(Operator op1,Operator op2)
{
if(op1.isTernary() && op2.isTernary()) {
if(op1 instanceof TernaryOperator.RhsTernaryOperator &&
op2 instanceof TernaryOperator.RhsTernaryOperator )
return true;
return false;
}
if(op1 == sentinel ) return false;
if(op2 == sentinel ) return true;
if(op2.isPrefix() && op1.isBinary()) return false;
if(op1.getPrecedence() < op2.getPrecedence()) return true;
if(op1.getPrecedence() == op2.getPrecedence() && op1.isLeftBinding()) return true;
return false;
}
If both operators are the right hand of a ternary operator then compareOps returns true one operator will be popped. Otherwise if both are the left hand ternary operators or one is a left and one is a right then compareOps returns false and no operators are popped.
The other bit of handling happens in the popOp method:
protected void popOp() throws ParseException
{
Operator op = ops.pop();
if(op == implicitMul) {
Node rhs = nodes.pop();
Node lhs = nodes.pop();
Node node = nf.buildOperatorNode(
jep.getOperatorTable().getMultiply(),
lhs, rhs);
nodes.push(node);
}
else if(op.isBinary()) {
Node rhs = nodes.pop();
Node lhs = nodes.pop();
Node node = nf.buildOperatorNode(op, lhs, rhs);
nodes.push(node);
}
else if(op.isUnary()) {
Node lhs = nodes.pop();
Node node = nf.buildOperatorNode(op, lhs);
nodes.push(node);
}
else if(op.isTernary() && op instanceof TernaryOperator.RhsTernaryOperator ) {
Operator op2 = ops.pop();
if(!(op2 instanceof TernaryOperator ) || !((TernaryOperator) op2).getRhsOperator().equals(op)) {
throw new ParseException(
MessageFormat.format(JepMessages.getString("configurableparser.ShuntingYard.NextOperatorShouldHaveBeenMatchingTernaryOp"),op2.getName())); //$NON-NLS-1$
}
Node rhs = nodes.pop();
Node middle = nodes.pop();
Node lhs = nodes.pop();
Node node = nf.buildOperatorNode(op2, new Node[]{lhs,middle,rhs});
nodes.push(node);
}
else {
throw new ParseException(MessageFormat.format(JepMessages.getString("configurableparser.ShuntingYard.InvalidOperatorOnStack"),op.getSymbol())); //$NON-NLS-1$
}
}
Only the right hand side of the ternary operator should be encountered. The operator directly below it should be the corresponding left hand operator. (Any operator with a lower precedence will have already been popped, the only operators with higher precedence are assignment operators which can't occur inside a ternary operator).
Both the left and right hands operators are now popped. I'm constructing a tree and the last three nodes produced are removed with a new ternary operator node constructed.