/*******************************************************************************
* This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* Peter Smith
*******************************************************************************/
package org.boris.expr.parser;
import java.io.IOException;
import java.util.ArrayList;
import org.boris.expr.Expr;
import org.boris.expr.ExprAddition;
import org.boris.expr.ExprArray;
import org.boris.expr.ExprDivision;
import org.boris.expr.ExprDouble;
import org.boris.expr.ExprEqual;
import org.boris.expr.ExprException;
import org.boris.expr.ExprExpression;
import org.boris.expr.ExprFunction;
import org.boris.expr.ExprGreaterThan;
import org.boris.expr.ExprGreaterThanOrEqualTo;
import org.boris.expr.ExprInteger;
import org.boris.expr.ExprLessThan;
import org.boris.expr.ExprLessThanOrEqualTo;
import org.boris.expr.ExprMissing;
import org.boris.expr.ExprMultiplication;
import org.boris.expr.ExprNotEqual;
import org.boris.expr.ExprPower;
import org.boris.expr.ExprString;
import org.boris.expr.ExprStringConcat;
import org.boris.expr.ExprSubtraction;
import org.boris.expr.ExprVariable;
import org.boris.expr.IBinaryOperator;
import org.boris.expr.IEvaluationCallback;
public class ExprParser
{
private Expr current;
private IParserVisitor visitor;
public static Expr parse(String text, IEvaluationCallback callback)
throws IOException, ExprException {
ExprParser p = new ExprParser();
p.parse(new ExprLexer(text), callback);
return p.get();
}
public void setParserVisitor(IParserVisitor visitor) {
this.visitor = visitor;
}
public void parse(ExprLexer lexer, IEvaluationCallback callback)
throws IOException, ExprException {
ExprToken e = null;
while ((e = lexer.next()) != null) {
parseToken(lexer, callback, e);
}
}
private void parseToken(ExprLexer lexer, IEvaluationCallback callback,
ExprToken token) throws ExprException, IOException {
switch (token.type) {
case Plus:
case Minus:
case Multiply:
case Divide:
case Power:
case StringConcat:
case LessThan:
case LessThanOrEqualTo:
case GreaterThan:
case GreaterThanOrEqualTo:
case Equal:
case NotEqual:
parseOperator(token);
break;
case Decimal:
case Integer:
case String:
case Variable:
parseValue(token, callback);
break;
case OpenBracket:
parseExpression(lexer, callback);
break;
case Function:
parseFunction(token, lexer, callback);
break;
case OpenBrace:
parseArray(lexer, callback);
break;
default:
throw new ExprException("Unexpected " + token.type + " found");
}
}
private void parseFunction(ExprToken token, ExprLexer lexer,
IEvaluationCallback callback) throws ExprException, IOException {
Expr c = current;
current = null;
ExprToken e = null;
ArrayList args = new ArrayList();
while ((e = lexer.next()) != null) {
if (e.type.equals(ExprTokenType.Comma)) {
if (current == null)
args.add(ExprMissing.MISSING);
else
args.add(current);
current = null;
} else if (e.type.equals(ExprTokenType.CloseBracket)) {
if (current != null)
args.add(current);
current = c;
break;
} else {
parseToken(lexer, callback, e);
}
}
ExprFunction f = new ExprFunction(callback, token.val, (Expr[]) args
.toArray(new Expr[0]));
if (visitor != null)
visitor.annotateFunction(f);
setValue(f);
}
private void parseExpression(ExprLexer lexer, IEvaluationCallback callback)
throws IOException, ExprException {
Expr c = current;
current = null;
ExprToken e = null;
while ((e = lexer.next()) != null) {
if (e.type.equals(ExprTokenType.CloseBracket)) {
Expr t = current;
current = c;
setValue(new ExprExpression(t));
break;
} else {
parseToken(lexer, callback, e);
}
}
}
private void parseArray(ExprLexer lexer, IEvaluationCallback callback)
throws ExprException, IOException {
Expr c = current;
current = null;
ExprToken e = null;
int cols = -1;
int count = 0;
ArrayList args = new ArrayList();
while ((e = lexer.next()) != null) {
if (e.type.equals(ExprTokenType.Comma)) {
if (current == null)
throw new ExprException(
"Arrays cannot contain empty values");
else
args.add(current);
current = null;
count++;
} else if (e.type.equals(ExprTokenType.SemiColon)) {
if (current == null)
throw new ExprException(
"Arrays cannot contain empty values");
else
args.add(current);
current = null;
count++;
if (count == 0) {
throw new ExprException(
"Array rows must contain at least one element");
}
if (cols != -1 && count != cols) {
throw new ExprException("Array rows must be equal sizes");
}
cols = count;
count = 0;
} else if (e.type.equals(ExprTokenType.CloseBrace)) {
if (current != null)
args.add(current);
current = c;
break;
} else {
parseToken(lexer, callback, e);
}
}
int rows = 1;
if (cols == -1)
cols = args.size();
else
rows = args.size() / cols;
ExprArray a = new ExprArray(rows, cols);
for (int i = 0; i < args.size(); i++) {
a.set(0, i, (Expr) args.get(i));
}
setValue(a);
}
private void parseValue(ExprToken e, IEvaluationCallback callback)
throws ExprException {
Expr value = null;
switch (e.type) {
case Decimal:
value = new ExprDouble(e.doubleValue);
break;
case Integer:
value = new ExprInteger(e.integerValue);
break;
case String:
value = new ExprString(e.val);
break;
case Variable:
value = new ExprVariable(callback, e.val);
if (visitor != null)
visitor.annotateVariable((ExprVariable) value);
break;
}
setValue(value);
}
private void setValue(Expr value) throws ExprException {
if (current == null) {
current = value;
return;
} else {
Expr c = current;
do {
if (!(c instanceof IBinaryOperator))
throw new ExprException("Expected operator not found");
Expr rhs = ((IBinaryOperator) c).getRHS();
if (rhs == null) {
((IBinaryOperator) c).setRHS(value);
return;
} else {
c = rhs;
}
} while (c != null);
throw new ExprException("Unexpected token found");
}
}
private void parseOperator(ExprToken e) throws ExprException {
switch (e.type) {
case Plus:
Expr lhs = current;
current = new ExprAddition(lhs, null);
break;
case Minus:
lhs = current;
current = new ExprSubtraction(lhs, null);
break;
case Multiply:
parseMultiplyDivide(new ExprMultiplication(null, null));
break;
case Divide:
parseMultiplyDivide(new ExprDivision(null, null));
break;
case Power:
parseMultiplyDivide(new ExprPower(null, null));
break;
case StringConcat:
parseMultiplyDivide(new ExprStringConcat(null, null));
break;
case LessThan:
current = new ExprLessThan(current, null);
break;
case LessThanOrEqualTo:
current = new ExprLessThanOrEqualTo(current, null);
break;
case GreaterThan:
current = new ExprGreaterThan(current, null);
break;
case GreaterThanOrEqualTo:
current = new ExprGreaterThanOrEqualTo(current, null);
break;
case NotEqual:
current = new ExprNotEqual(current, null);
break;
case Equal:
current = new ExprEqual(current, null);
break;
default:
throw new ExprException("Unhandled operator type: " + e.type);
}
}
private void parseMultiplyDivide(IBinaryOperator md) throws ExprException {
if (current == null)
throw new ExprException("Unexpected null token");
Expr c = current;
Expr prev = null;
while (c != null) {
if (c instanceof ExprAddition || c instanceof ExprSubtraction) {
prev = c;
c = ((IBinaryOperator) c).getRHS();
} else {
if (prev == null) {
md.setLHS(current);
current = (Expr) md;
break;
} else {
IBinaryOperator b = (IBinaryOperator) prev;
md.setLHS(b.getRHS());
b.setRHS((Expr) md);
break;
}
}
}
}
public Expr get() {
return current;
}
}