package splar.plugins.reasoners.bdd.javabdd;
import java.util.ArrayList;
import java.util.Map;
import splar.core.constraints.BooleanVariable;
import splar.core.constraints.BooleanVariableInterface;
import net.sf.javabdd.BDD;
import net.sf.javabdd.BDDFactory;
/*
* Propositional Formulas Grammar
* ------------------------------
* F ::= p | ~F | F op F | (F)
* op ::= | | & | -> | <-> | OR | AND | IMP | BIIMP (case INsensitive)
* p ::= variable (letter + letter|digit|_)
*
* Remarks:
* Operator "~" should only precede variables (never formulas)
*
*/
public class PF2BDDParser {
private long maxParsingTimeAllowed;
private long parsingTime;
private int index = 0;
private static final int AND = 1;
private static final int OR = 2;
private static final int IMP = 3;
private static final int BIIMP = 4;
private static String letters = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static String digits = "0123456789";
private static String others = "_";
private String all = letters + digits + others;
private BDDFactory bddFactory;
protected Map<String,Integer> variable2indexMap;
protected ArrayList<BooleanVariableInterface> variables;
public PF2BDDParser(BDDFactory bddFactory, Map<String,Integer> variable2indexMap, long maxParsingTimeAllowed) {
this.bddFactory = bddFactory;
this.variable2indexMap = variable2indexMap;
variables = new ArrayList<BooleanVariableInterface>();
this.maxParsingTimeAllowed = maxParsingTimeAllowed;
this.parsingTime = -1;
}
public long getPFParsingTime() {
return parsingTime;
}
public BDD parse(String formula) throws Exception {
index = 0;
long start = System.currentTimeMillis();
BDD bdd = F(formula.trim(), start);
parsingTime = System.currentTimeMillis() - start;
return bdd;
}
private String currentChar(String formula) {
return formula.substring(index,index+1);
}
private BDD F(String formula, long start) throws Exception {
BDD bdd = bddFactory.one();
BDD tempBDD1, tempBDD2;
long elapsedTime = (System.currentTimeMillis() - start);
if ( elapsedTime > maxParsingTimeAllowed ) {
throw new BDDExceededBuildingTimeException("PF2BDDParser: Maximum time allowed for BDD construction exceeded: " + maxParsingTimeAllowed + " ms", "");
}
// end of the formula
if (!EOF(formula)) {
// operator !
if (currentChar(formula).equals("~")) {
incrementIndex(formula);
skipBlanks(formula);
if ( isLetter(currentChar(formula)) ){
int varIndex = extractVar(formula, false);
if ( varIndex == -1 ) {
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
tempBDD1 = bddFactory.ithVar(varIndex);
}
else {
tempBDD1 = F(formula, start);
}
tempBDD1 = tempBDD1.not();
skipBlanks(formula);
if (!EOF(formula)) {
if (isLetter(currentChar(formula)) || currentChar(formula).equals("&") || currentChar(formula).equals("|") ||
currentChar(formula).equals("-") ||currentChar(formula).equals("<")) {
int op = operator(formula);
skipBlanks(formula);
tempBDD2 = F(formula, start);
bdd.andWith(applyBDDOp(tempBDD1, tempBDD2, op, start));
}
else {
bdd.andWith(tempBDD1);
}
}
else {
bdd.andWith(tempBDD1);
}
}
// parenthesis "("
else if (currentChar(formula).equals("(")) {
incrementIndex(formula);
skipBlanks(formula);
tempBDD1 = F(formula, start);
if (!currentChar(formula).equals(")")){
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
incrementIndex(formula);
if (!EOF(formula)) {
skipBlanks(formula);
if (isLetter(currentChar(formula))||currentChar(formula).equals("&") || currentChar(formula).equals("|") ||
currentChar(formula).equals("-") ||currentChar(formula).equals("<")) {
int op = operator(formula);
skipBlanks(formula);
tempBDD2 = F(formula, start);
bdd.andWith(applyBDDOp(tempBDD1, tempBDD2, op, start));
}
else if (!currentChar(formula).equals(")")) {
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
else {
bdd.andWith(tempBDD1);
}
}
else {
bdd.andWith(tempBDD1);
}
}
// variable name
else {
int varIndex = extractVar(formula, true);
if ( varIndex == -1 ) {
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
tempBDD1 = bddFactory.ithVar(varIndex);
skipBlanks(formula);
if ( !EOF(formula) && !currentChar(formula).startsWith(")")) {
int op = operator(formula);
skipBlanks(formula);
tempBDD2 = F(formula, start);
bdd.andWith(applyBDDOp(tempBDD1, tempBDD2, op, start));
}
else {
bdd.andWith(tempBDD1);
}
}
}
return bdd;
}
private BDD applyBDDOp(BDD bdd1, BDD bdd2, int op, long start) throws Exception {
long elapsedTime = (System.currentTimeMillis() - start);
if ( elapsedTime > maxParsingTimeAllowed ) {
throw new BDDExceededBuildingTimeException("PF2BDDParser: Maximum time allowed for BDD construction exceeded: " + maxParsingTimeAllowed + " ms", "");
}
BDD resultBDD = bddFactory.one();
switch(op) {
case AND:
resultBDD.andWith(bdd1.and(bdd2));
break;
case OR:
resultBDD.andWith(bdd1.or(bdd2));
break;
case IMP:
resultBDD.andWith(bdd1.imp(bdd2));
break;
case BIIMP:
resultBDD.andWith(bdd1.biimp(bdd2));
break;
}
return resultBDD;
}
private int operator(String formula) throws Exception {
int op = -1;
if ( isLetter(currentChar(formula))) {
String opStr = extractOperator(formula);
if ( opStr.compareToIgnoreCase("and") == 0 ) {
op = AND;
}
else if ( opStr.compareToIgnoreCase("or") == 0 ) {
op = OR;
}
else if ( opStr.compareToIgnoreCase("imp") == 0 ) {
op = IMP;
}
else if ( opStr.compareToIgnoreCase("biimp") == 0 ) {
op = BIIMP;
}
else {
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
}
else if ( currentChar(formula).startsWith("|")) {
incrementIndex(formula);
op = OR;
}
else if (currentChar(formula).startsWith("&")) {
incrementIndex(formula);
op = AND;
}
else if (currentChar(formula).startsWith("-")) {
incrementIndex(formula);
if (!currentChar(formula).startsWith(">")) {
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
incrementIndex(formula);
op = IMP;
}
else if (currentChar(formula).startsWith("<")) {
incrementIndex(formula);
if (!currentChar(formula).startsWith("-")) {
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
incrementIndex(formula);
if (!currentChar(formula).startsWith(">")) {
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
incrementIndex(formula);
op = BIIMP;
}
else {
throw new Exception("Error on formula: " + formula + " at index (" + index + ")");
}
return op;
}
private String extractOperator(String formula) {
StringBuffer opName = new StringBuffer();
if ( isLetter(currentChar(formula)) ) {
opName.append(currentChar(formula));
incrementIndex(formula);
while( !EOF(formula) && isValidChar(currentChar(formula))) {
opName.append(currentChar(formula));
incrementIndex(formula);
}
}
return opName.toString();
}
private int extractVar(String formula, boolean varState) {
StringBuffer varName = new StringBuffer();
if ( isLetter(currentChar(formula)) ) {
varName.append(currentChar(formula));
incrementIndex(formula);
while( !EOF(formula) && isValidChar(currentChar(formula))) {
varName.append(currentChar(formula));
incrementIndex(formula);
}
int index = createVarIndex(varName.toString());
if ( index != -1 ) {
BooleanVariable variable = new BooleanVariable(varName.toString());
variable.setState(varState);
variables.add(variable);
}
return index;
}
return -1;
}
public ArrayList<BooleanVariableInterface> getVariables() {
return variables;
}
private int createVarIndex(String varName) {
Integer index = variable2indexMap.get(varName);
if (index != null ){
return index.intValue();
}
return -1;
}
private boolean EOF(String formula) {
if ( index >= formula.length() ) {
return true;
}
return false;
}
private boolean isLetter(String str) {
if (letters.indexOf(str)!= -1) {
return true;
}
return false;
}
private boolean isValidChar(String str) {
if (all.indexOf(str) != -1) {
return true;
}
return false;
}
private void skipBlanks(String formula) {
if (!EOF(formula)) {
while (currentChar(formula).startsWith(" ")) {
incrementIndex(formula);
}
}
}
private void incrementIndex(String formula) {
index++;
}
}