import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
import java.util.function.BiPredicate;
public class LogicalTerm {
public static interface Expression {
boolean eval();
}
public static class BinaryExpression implements Expression {
private final Expression left;
private final Expression right;
private final char operator;
private final BiPredicate<Expression, Expression> pred;
BinaryExpression(Expression left, Expression right, char operator, BiPredicate<Expression, Expression> pred) {
this.operator = operator;
this.left = left;
this.right = right;
this.pred = pred;
}
public Expression getLeft() { return left; }
public Expression getRight() { return right; }
@Override
public boolean eval() {
return pred.test(left, right);
}
@Override
public String toString() {
return left.toString() + " " + operator + " " + right.toString();
}
}
public static class ParenExpression implements Expression {
private final Expression expr;
ParenExpression(Expression expr) {
this.expr = expr;
}
public Expression getExpression() { return expr; }
@Override
public boolean eval() {
return expr.eval();
}
@Override
public String toString() {
return "(" + expr.toString() + ")";
}
}
public static class UnaryExpression implements Expression {
private final Expression expr;
private final char operator;
private final Predicate<Expression> pred;
UnaryExpression(Expression expr, char operator, Predicate<Expression> pred) {
this.operator = operator;
this.expr = expr;
this.pred = pred;
}
public Expression getExpression() { return expr; }
@Override
public boolean eval() {
return pred.test(expr);
}
@Override
public String toString() {
return operator + expr.toString();
}
}
public static class Literal implements Expression {
private final boolean value;
public Literal(boolean value) { this.value = value; }
@Override
public boolean eval() {
return value;
}
@Override
public String toString() {
return Boolean.toString(value);
}
}
public static class Identifier implements Expression {
private final String name;
private boolean value;
Identifier(String name) { this.name = name; }
public String getName() { return name; }
public void setValue(boolean value) { this.value = value; }
public boolean eval() {
return value;
}
@Override
public String toString() {
return name;
}
}
private static final String FALSE = "false";
private static final String TRUE = "true";
public static final char OR = '\u2228';
public static final char AND = '\u2227';
public static final char NOT = '\u00ac';
private static final char LEFT_PAREN = '(';
private static final char RIGHT_PAREN = ')';
private final String text;
private int pos;
private int ch;
private Map<String, Identifier> identifiers = new HashMap<>();
public LogicalTerm(String text) {
this.text = text;
}
public Collection<Identifier> getIdentifiers() {
return identifiers.values();
}
public Identifier getIdentifier(String name) {
return identifiers.get(name);
}
public Expression compile() {
pos = -1;
advance();
return parse();
}
private Expression parse() {
Expression expression = parseTerm();
while (true) {
if (eat(OR)) {
expression = or(expression, parseTerm());
} else {
return expression;
}
}
}
private Expression parseTerm() {
Expression expression = parseOperand();
while (true) {
if (eat(AND)) {
expression = and(expression, parseOperand());
} else {
return expression;
}
}
}
private Expression parseOperand() {
if (eat(NOT)) return not(parseOperand());
int startPos = pos;
if (eat(LEFT_PAREN)) {
Expression expression = parse();
if (!eat(RIGHT_PAREN)) {
throw new RuntimeException("Missing ')' at " + pos);
}
return new ParenExpression(expression);
} else if (Character.isLetter(ch)) {
while (Character.isLetter(ch) || Character.isDigit(ch)) {
advance();
}
String token = text.substring(startPos, pos);
if (TRUE.equalsIgnoreCase(token)) return literal(true);
else if (FALSE.equalsIgnoreCase(token)) return literal(false);
identifiers.putIfAbsent(token, new Identifier(token));
return identifiers.get(token);
} else {
throw new RuntimeException("Unexpected character " + ch + " at " + pos);
}
}
private void advance() {
int max = text.length();
if (pos < max) {
pos++;
}
if (pos < max) {
ch = text.charAt(pos);
} else {
ch = -1;
}
}
private boolean eat(char expected) {
while (Character.isWhitespace(ch)) advance();
if (ch == expected) {
advance();
return true;
}
return false;
}
private Expression or(Expression left, Expression right) {
return new BinaryExpression(left, right, OR, (l, r) -> l.eval() || r.eval());
}
private Expression and(Expression left, Expression right) {
return new BinaryExpression(left, right, AND, (l, r) -> l.eval() && r.eval());
}
private Expression not(Expression expr) {
return new UnaryExpression(expr, NOT, (e) -> !e.eval());
}
private Expression literal(boolean value) {
return new Literal(value);
}
}