/*
* xtc - The eXTensible Compiler
* Copyright (C) 2009-2012 New York University
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.lang.cpp;
import java.util.List;
import java.util.Iterator;
import java.util.Set;
import xtc.tree.GNode;
import xtc.tree.Node;
import xtc.tree.Token;
import xtc.tree.Visitor;
import xtc.type.C;
import xtc.type.Type;
import xtc.lang.cpp.PresenceConditionManager.PresenceCondition;
import xtc.lang.cpp.MacroTable.Macro;
import xtc.lang.cpp.MacroTable.Entry;
import xtc.lang.cpp.Syntax.Kind;
import net.sf.javabdd.BDD;
import net.sf.javabdd.BDDFactory;
/**
* Generate a BDD from a preprocessor conditional expression.
*
* @author Paul Gazzillo
*/
public class ConditionEvaluator {
/** Manages presence conditions. */
protected PresenceConditionManager presenceConditionManager;
/** An optional macro table used for the defined operator. */
private MacroTable macroTable;
/** The expression parser. */
private ExpressionParser parser;
/** The BDD factory. */
private BDDFactory B;
/** The common type operations for C. */
private final C cops;
/**
* True when the evaluator is constructing a string version of a
* subexpression. This is necessary to represent non-boolean
* subexpression as variables in a boolean function.
*/
private boolean dostring = false;
/** Flag for non-boolean subexpressions. */
private boolean nonboolean = false;
/** Setting that pulls all undefined macros to false and empty. */
private boolean pullUndefinedFalse = false;
/** An optional set of free configs seen. */
private Set<String> seenConfigs = null;
/** Restrict free macros to the given prefix. */
private boolean enableRestrictPrefix = false;
/** The prefix to restrict free macros to. */
private String restrictPrefix = null;
/**
* Set the pullUndefinedFalse flag.
*
* @param b The new setting.
*/
public void setPullUndefinedFalse(boolean b) {
this.pullUndefinedFalse = b;
}
/**
* Save free configs to the given list.
*
* @param seenConfigs The set of free configs.
*/
public void setSeenConfigs(Set<String> seenConfigs) {
this.seenConfigs = seenConfigs;
}
/** Stop saving free configs. */
public void unsetSeenConfigs() {
this.seenConfigs = null;
}
/**
* Restrict free macros to the given prefix.
*
* @param The prefix to restrict or null for no restriction.
*/
public void restrictPrefix(String prefix) {
if (null == prefix) {
enableRestrictPrefix = false;
this.restrictPrefix = null;
} else {
enableRestrictPrefix = true;
this.restrictPrefix = prefix;
}
}
/**
* Create a new condition evaluator.
*
* @param presenceConditionManager Manages presence conditions.
* @param macroTable The macro table.
*/
public ConditionEvaluator(ExpressionParser parser,
PresenceConditionManager presenceConditionManager,
MacroTable macroTable) {
this.presenceConditionManager = presenceConditionManager;
this.macroTable = macroTable;
this.parser = parser;
this.B = presenceConditionManager.getBDDFactory();
this.cops = new C();
}
/**
* Create a new condition evaluator without macro resolution.
*
* @param presenceConditionManager Manages presence conditions.
*/
public ConditionEvaluator(ExpressionParser parser,
PresenceConditionManager presenceConditionManager) {
this(parser, presenceConditionManager, null);
}
/**
* Evaluate a conditional expression.
*
* @param expression An expression.
* @return A BDD.
*/
public BDD evaluate(Iterator<Syntax> expression) {
return evaluate(parser.parse(expression));
}
/**
* Evaluate a conditional expression's AST. This method can be used
* without passing a parser.
*
* @param ast An AST of an expression.
* @return A BDD.
*/
public BDD evaluate(Node ast) {
nonboolean = false;
dostring = false;
if (null == ast) {
return B.zero();
} else {
return ensureBDD(visitor.dispatch(ast));
}
}
/**
* Return true if the expression had a non-boolean subexpression.
*
* @return true if there was a non-boolean subexpression.
*/
public boolean sawNonboolean() {
return nonboolean;
}
private Visitor visitor = new Visitor() {
/** Process an integer constant. */
public Object visitIntegerConstant(GNode n) {
xtc.type.Type result;
result = cops.typeInteger(n.getString(0));
return result.getConstant().bigIntValue().longValue();
}
/** Process a character constant. */
public Object visitCharacterConstant(GNode n) {
xtc.type.Type result;
result = cops.typeCharacter(n.getString(0));
return result.getConstant().bigIntValue().longValue();
}
/** Process primary identifier. */
public Object visitPrimaryIdentifier(GNode n) {
// if (enableRestrictPrefix && ! n.getString(0).startsWith(restrictPrefix)) {
// return "";
// }
// if (pullUndefinedFalse) {
// return "";
// }
if (null != seenConfigs) {
seenConfigs.add(n.getString(0));
}
return n.getString(0);
}
/** Process a unary minus. */
public Object visitUnaryMinusExpression(GNode n) {
Object a;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
dostring = false;
if (a instanceof Long) {
return - (Long) a;
}
else {
return "- " + parens(a);
}
}
/** Process a unary plus. */
public Object visitUnaryPlusExpression(GNode n) {
nonboolean = true;
return dispatch(n.getGeneric(0));
}
/** Process a logical negation. */
public Object visitLogicalNegationExpression(GNode n) {
if (dostring) {
Object a = dispatch(n.getGeneric(0));
if (a instanceof Long) {
return "" + ((((Long) a).equals(new Long(0))) ? 1 : 0);
}
else {
return "! " + parens(a);
}
}
else {
BDD a = ensureBDD(dispatch(n.getGeneric(0)));
BDD bdd;
bdd = a.not();
a.free();
return bdd;
}
}
/** Process a bitwise negation. */
public Object visitBitwiseNegationExpression(GNode n) {
Object a, result;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
dostring = false;
if (a instanceof Long) {
result = ~ (Long) a;
}
else {
return "~ " + parens(a);
}
return result;
}
/** Process a multiplicative operation. */
public Object visitMultiplicativeExpression(GNode n) {
Object a, b, result;
String op;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
b = dispatch(n.getGeneric(2));
op = n.getString(1);
dostring = false;
if (a instanceof Long && b instanceof Long) {
if ((op.equals("/")
|| op.equals("%")) && ((Long) b).equals(new Long(0))) {
System.err.println("division by zero in #if");
result = new Long(0);
}
if (op.equals("*")) {
result = (Long) a * (Long) b;
}
else if (op.equals("/")) {
result = (Long) a / (Long) b;
}
else if (op.equals("%")) {
result = (Long) a % (Long) b;
}
else {
result = "";
}
}
else {
result = parens(a) + " " + op + " " + parens(b);
}
return result;
}
/** Process an additive operation. */
public Object visitAdditiveExpression(GNode n) {
Object a, b, result;
String op;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
b = dispatch(n.getGeneric(2));
op = n.getString(1);
dostring = false;
if (a instanceof Long && b instanceof Long) {
if (op.equals("+")) {
result = (Long) a + (Long) b;
}
else if (op.equals("-")) {
result = (Long) a - (Long) b;
}
else {
result = "";
}
}
else {
result = parens(a) + " " + op + " " + parens(b);
}
return result;
}
/** Process a shift expression. */
public Object visitShiftExpression(GNode n) {
Object a, b, result;
String op;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
b = dispatch(n.getGeneric(2));
op = n.getString(1);
dostring = false;
if (a instanceof Long && b instanceof Long) {
if (op.equals("<<")) {
result = (Long) a << (Long) b;
}
else if (op.equals(">>")) {
result = (Long) a >> (Long) b;
}
else {
result = "";
}
}
else {
result = parens(a) + " " + op + " " + parens(b);
}
return result;
}
/** Process a relational expression. */
public Object visitRelationalExpression(GNode n) {
Object a, b, result;
String op;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
b = dispatch(n.getGeneric(2));
op = n.getString(1);
dostring = false;
if (a instanceof Long && b instanceof Long) {
Long x = (Long) a;
Long y = (Long) b;
long zero = 0;
long one = 1;
if (op.equals("<")) {
result = x < y ? one : zero;
}
else if (op.equals("<=")) {
result = x <= y ? one : zero;
}
else if (op.equals(">")) {
result = x > y ? one : zero;
}
else if (op.equals(">=")) {
result = x >= y ? one : zero;
}
else {
result = "";
}
}
else {
result = parens(a) + " " + op + " " + parens(b);
}
return result;
}
/** Process a equality expression. */
public Object visitEqualityExpression(GNode n) {
Object a, b, result;
String op;
boolean mydostring = dostring;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
b = dispatch(n.getGeneric(2));
op = n.getString(1);
dostring = false;
if (a instanceof Long && b instanceof Long) {
if (op.equals("==")) {
result = ((Long) a).equals((Long) b);
}
else if (op.equals("!=")) {
result = ! ((Long) a).equals((Long) b);
}
else {
result = "";
}
}
else {
String sa, sb;
if (a instanceof String) {
sa = (String) a;
}
else if (a instanceof Long) {
sa = ((Long) a).toString();
}
else {
return null;
}
if (b instanceof String) {
sb = (String) b;
}
else if (b instanceof Long) {
sb = ((Long) b).toString();
}
else {
return null;
}
if (op.equals("==") && sa.equals(sb)) {
result = mydostring ? "1" : B.one();
}
else {
result = parens(sa) + " " + op + " " + parens(sb);
}
}
return result;
}
/** Process a bitwise and. */
public Object visitBitwiseAndExpression(GNode n) {
Object a, b, result;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
b = dispatch(n.getGeneric(1));
dostring = false;
if (a instanceof Long && b instanceof Long) {
result = (Long) a & (Long) b;
}
else {
result = parens(a) + " & " + parens(b);
}
return result;
}
/** Process a bitwise xor. */
public Object visitBitwiseXorExpression(GNode n) {
Object a, b, result;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
b = dispatch(n.getGeneric(1));
dostring = false;
if (a instanceof Long && b instanceof Long) {
result = (Long) a ^ (Long) b;
}
else {
result = parens(a) + " ^ " + parens(b);
}
return result;
}
/** Process a bitwise or. */
public Object visitBitwiseOrExpression(GNode n) {
Object a, b, result;
nonboolean = true;
dostring = true;
a = dispatch(n.getGeneric(0));
b = dispatch(n.getGeneric(1));
dostring = false;
if (a instanceof Long && b instanceof Long) {
result = (Long) a | (Long) b;
}
else {
result = parens(a) + " | " + parens(b);
}
return result;
}
/** Process a logical and. */
public Object visitLogicalAndExpression(GNode n) {
if (dostring) {
Object a = dispatch(n.getGeneric(0));
Object b = dispatch(n.getGeneric(1));
if (a instanceof Long && b instanceof Long) {
return (Long) a & (Long) b;
}
else {
return parens(a) + " && " + parens(b);
}
}
else {
BDD a, b, bdd;
a = ensureBDD(dispatch(n.getGeneric(0)));
b = ensureBDD(dispatch(n.getGeneric(1)));
bdd = a.andWith(b);
return bdd;
}
}
/** Process a logical or. */
public Object visitLogicalOrExpression(GNode n) {
if (dostring) {
Object a = dispatch(n.getGeneric(0));
Object b = dispatch(n.getGeneric(1));
if (a instanceof Long && b instanceof Long) {
return (Long) a | (Long) b;
}
else {
return parens(a) + " || " + parens(b);
}
}
else {
BDD a, b, bdd;
a = ensureBDD(dispatch(n.getGeneric(0)));
b = ensureBDD(dispatch(n.getGeneric(1)));
bdd = a.orWith(b);
return bdd;
}
}
/** Make a new BDD argument, "defined M". If a macro table was supplied
* to the evaluator, look for M there and evaluate the operation.
*/
public Object visitDefinedExpression(GNode n) {
String parameter = n.getGeneric(0).getString(0);
//evaluate the defined operation, preserving configurations
if (macroTable != null) {
List<Entry> definitions = macroTable.get(parameter, presenceConditionManager);
if (definitions != null && definitions.size() > 0) {
boolean hasDefined, hasUndefined, hasFree;
//three conditions
//1) defined under all configurations, so output 1 (true)
//2) undefined under all configurations, so output 0 (false)
//3) partially defined, so output union of configurations
hasDefined = false;
hasUndefined = false;
hasFree = false;
for (Entry def : definitions) {
if (def.macro.state == Macro.State.FREE) {
hasFree = true;
}
else if (def.macro.state == Macro.State.DEFINED) {
hasDefined = true;
}
else if (def.macro.state == Macro.State.UNDEFINED) {
hasUndefined = true;
}
}
if (hasDefined && ! hasUndefined && ! hasFree) {
//fully defined in this presenceCondition
return B.one(); //the constant true BDD
} else if (hasUndefined && ! hasDefined && ! hasFree) {
//not defined in this presenceCondition
return B.zero(); //the constant false BDD
} else {
if (null != seenConfigs) {
seenConfigs.add(PresenceConditionManager.Variables.
createDefinedVariable(parameter));
for (Entry def : definitions) {
if (def.macro.state == Macro.State.FREE) {
seenConfigs.addAll(def.presenceCondition.getAllConfigs());
}
}
}
//partially defined in this presenceCondition
BDD defined = B.zero();
List<Token> tokenlist;
int c;
for (Entry def : definitions) {
if (def.macro.state == Macro.State.FREE) {
BDD newDefined;
BDD varBDD;
BDD term;
// pull FREE to false also if pullUndefinedFalse is
// set
if (!pullUndefinedFalse) {
varBDD = presenceConditionManager.getVariableManager()
.getDefinedVariable(parameter);
term = def.presenceCondition.getBDD().and(varBDD);
newDefined = defined.or(term);
term.free();
defined.free();
varBDD.free();
defined = newDefined;
} else {
System.err.println("pullUndefinedFalse");
}
} else if (def.macro.state == Macro.State.DEFINED) {
BDD newDefined;
newDefined = defined.or(def.presenceCondition.getBDD());
defined.free();
defined = newDefined;
} else if (def.macro.state == Macro.State.UNDEFINED) {
//do nothing
}
}
return defined;
} //end partially defined
} else {
// The macro was used in a conditional expression before or
// without being defined, therefore it is a configuration
// variable.
if (macroTable.getConfigurationVariables()) {
macroTable.configurationVariables.add(parameter);
}
if (null != seenConfigs) {
seenConfigs.add(PresenceConditionManager.Variables.
createDefinedVariable(parameter));
}
if (enableRestrictPrefix && ! parameter.startsWith(restrictPrefix)) {
// System.err.println("restricting " + parameter + " to undefined");
return B.zero();
}
if (pullUndefinedFalse) {
return B.zero();
}
}
} //end has macro table
//if there are no macro table entries, just return the operation as is
return "defined " + parameter; //return a string
}
/** Process a conditional expression. */
public Object visitConditionalExpression(GNode n) {
if (dostring) {
Object a = dispatch(n.getGeneric(0));
Object b = dispatch(n.getGeneric(1));
Object c = dispatch(n.getGeneric(2));
if (a instanceof Long) {
return (! ((Long) a).equals(new Long(0))) ? b : c;
}
else {
return parens(a) + " ? " + parens(b) + " : " + parens(c);
}
}
else {
BDD a = ensureBDD(dispatch(n.getGeneric(0)));
BDD b = ensureBDD(dispatch(n.getGeneric(1)));
BDD c = ensureBDD(dispatch(n.getGeneric(2)));
BDD ab, na, nac, bdd;
//implement with a & b | !a & c
ab = a.and(b);
b.free();
na = a.not();
a.free();
nac = na.and(c);
c.free();
na.free();
bdd = ab.or(nac);
nac.free();
ab.free();
return bdd;
}
}
};
/** Ensures that parentheses surround terms to preserve order of operations
*/
public String parens(Object a) {
String s;
if (a instanceof String) {
s = (String) a;
}
else if (a instanceof Long) {
s = ((Long) a).toString();
}
else {
return null;
}
if (s.indexOf(" ") >= 0) {
return "(" + s + ")";
}
else {
return s;
}
}
/** Takes whatever the evaluation returns, string, boolean, or integer, and
* creates a BDD out of it if isn't already.
*/
public BDD ensureBDD(Object o) {
if (o instanceof BDD) {
return (BDD) o;
}
else if (o instanceof Long) {
if (((Long) o).equals(new Long(0))) {
return B.zero();
}
else {
return B.one();
}
}
else if (o instanceof String) {
String s = parens(o);
return presenceConditionManager.getVariableManager().getVariable(s);
}
else if (o instanceof Boolean) {
Boolean b = (Boolean) o;
if (b) {
return B.one();
}
else {
return B.zero();
}
}
else {
System.err.println("FATAL: ensureBDD, unforeseen type from evaluator");
System.err.println(o);
System.err.println(o.getClass());
System.exit(-1);
return null;
}
}
}