initial commit
This commit is contained in:
33
src/main/java/jef/expressions/AndExpression.java
Normal file
33
src/main/java/jef/expressions/AndExpression.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class AndExpression implements Expression {
|
||||
private final List<Expression> exprs;
|
||||
|
||||
public AndExpression(Expression... exprs) {
|
||||
this.exprs = List.of(exprs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.AND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return exprs.stream().map(e -> {
|
||||
if (e instanceof OrExpression) {
|
||||
return "(" + e + ")";
|
||||
} else {
|
||||
return e.toString();
|
||||
}
|
||||
}).collect(Collectors.joining(" AND "));
|
||||
}
|
||||
}
|
||||
60
src/main/java/jef/expressions/BinaryExpression.java
Normal file
60
src/main/java/jef/expressions/BinaryExpression.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class BinaryExpression implements Expression {
|
||||
private final Expression left;
|
||||
private final Expression right;
|
||||
private final Operator operator;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.BINARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return left + " " + operator + " " + right;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum Operator {
|
||||
EQ("="),
|
||||
NE("<>"),
|
||||
LT("<"),
|
||||
LE("<="),
|
||||
GT(">"),
|
||||
GE(">="),
|
||||
// OR("OR"),
|
||||
// AND("AND"),
|
||||
IN("IN"),
|
||||
;
|
||||
private final String string;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string;
|
||||
}
|
||||
|
||||
private static final Map<Operator, Operator> INVERSION = Map.of(
|
||||
EQ, NE,
|
||||
NE, EQ,
|
||||
LT, GE,
|
||||
GE, LT,
|
||||
LE, GT,
|
||||
GT, LE);
|
||||
|
||||
public Operator invert() {
|
||||
return INVERSION.get(this);
|
||||
}
|
||||
|
||||
public boolean isInvertible() {
|
||||
return INVERSION.containsKey(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/main/java/jef/expressions/ConstantExpression.java
Normal file
23
src/main/java/jef/expressions/ConstantExpression.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class ConstantExpression implements Expression {
|
||||
public static final jef.expressions.ConstantExpression I0 = new jef.expressions.ConstantExpression(0);
|
||||
public static final jef.expressions.ConstantExpression I1 = new jef.expressions.ConstantExpression(1);
|
||||
|
||||
protected final Object value;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.CONSTANT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
19
src/main/java/jef/expressions/Expression.java
Normal file
19
src/main/java/jef/expressions/Expression.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package jef.expressions;
|
||||
|
||||
public interface Expression {
|
||||
Type getType();
|
||||
|
||||
public enum Type {
|
||||
AND,
|
||||
BINARY,
|
||||
CONSTANT,
|
||||
FIELD,
|
||||
OR,
|
||||
PARAMETER,
|
||||
SELECT,
|
||||
TABLE,
|
||||
TERNARY,
|
||||
UNARY,
|
||||
WHERE,
|
||||
}
|
||||
}
|
||||
23
src/main/java/jef/expressions/FieldExpression.java
Normal file
23
src/main/java/jef/expressions/FieldExpression.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class FieldExpression extends ConstantExpression implements Expression {
|
||||
private final String name;
|
||||
|
||||
public FieldExpression(String name) {
|
||||
super(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.FIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
27
src/main/java/jef/expressions/OrExpression.java
Normal file
27
src/main/java/jef/expressions/OrExpression.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class OrExpression implements Expression {
|
||||
private final List<Expression> exprs;
|
||||
|
||||
public OrExpression(Expression... exprs) {
|
||||
this.exprs = List.of(exprs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.OR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return exprs.stream().map(Expression::toString).collect(Collectors.joining(" OR "));
|
||||
}
|
||||
}
|
||||
35
src/main/java/jef/expressions/ParameterExpression.java
Normal file
35
src/main/java/jef/expressions/ParameterExpression.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
public class ParameterExpression extends ConstantExpression implements Expression {
|
||||
private final int index;
|
||||
private final boolean isInput;
|
||||
|
||||
public ParameterExpression(int index, Object value, boolean isInput) {
|
||||
super(value);
|
||||
this.index = index;
|
||||
this.isInput = isInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.PARAMETER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isInput) {
|
||||
return "param #" + index;
|
||||
} else if (this.value == null) {
|
||||
return "null";
|
||||
} else if (this.value instanceof Collection) {
|
||||
return "(" + ((Collection) this.value).stream().map(Object::toString).collect(Collectors.joining(",")) + ")";
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
27
src/main/java/jef/expressions/SelectExpression.java
Normal file
27
src/main/java/jef/expressions/SelectExpression.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class SelectExpression implements Expression {
|
||||
private final List<String> fields;
|
||||
private final Expression from;
|
||||
private final String fromAlias;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.SELECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SELECT " + fields.stream().map(e -> e.equals("*") ? e : "`" + e + "`").collect(Collectors.joining(", ")) + " FROM "
|
||||
+ (!(from instanceof TableExpression) ? "(" + from + ")" : from)
|
||||
+ ((fromAlias == null || fromAlias.isBlank()) ? "" : " " + fromAlias);
|
||||
}
|
||||
}
|
||||
28
src/main/java/jef/expressions/TableExpression.java
Normal file
28
src/main/java/jef/expressions/TableExpression.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class TableExpression extends ConstantExpression {
|
||||
private final String name;
|
||||
|
||||
public TableExpression(String name) {
|
||||
super(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.TABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return (String) super.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "`" + super.toString() + "`";
|
||||
}
|
||||
}
|
||||
24
src/main/java/jef/expressions/TernaryExpression.java
Normal file
24
src/main/java/jef/expressions/TernaryExpression.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class TernaryExpression implements Expression {
|
||||
private final Expression cond;
|
||||
private final Expression whenTrue;
|
||||
private final Expression whenFalse;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.TERNARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return cond
|
||||
+ " ? " + (!(whenTrue instanceof ConstantExpression) ? "(" + whenTrue + ")" : whenTrue)
|
||||
+ " : " + (!(whenFalse instanceof ConstantExpression) ? "(" + whenFalse + ")" : whenFalse);
|
||||
}
|
||||
}
|
||||
35
src/main/java/jef/expressions/UnaryExpression.java
Normal file
35
src/main/java/jef/expressions/UnaryExpression.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class UnaryExpression implements Expression {
|
||||
private final Expression expr;
|
||||
private final Operator operator;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.UNARY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return operator + " (" + expr + ")";
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum Operator {
|
||||
NOT("NOT"),
|
||||
// NEG("-"),
|
||||
// POS("+"),
|
||||
;
|
||||
private final String string;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/main/java/jef/expressions/WhereExpression.java
Normal file
21
src/main/java/jef/expressions/WhereExpression.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package jef.expressions;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class WhereExpression implements Expression {
|
||||
private Expression queryable;
|
||||
private Expression where;
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.WHERE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return queryable + " WHERE " + where;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.FieldExpression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.ParameterExpression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.TableExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
import jef.expressions.WhereExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public abstract class ExpressionModifier {
|
||||
|
||||
public Expression modify(Expression expr) {
|
||||
return switch (expr.getType()) {
|
||||
case AND -> modifyAnd((AndExpression) expr);
|
||||
case BINARY -> modifyBinary((BinaryExpression) expr);
|
||||
case CONSTANT -> modifyConstant((ConstantExpression) expr);
|
||||
case FIELD -> modifyField((FieldExpression) expr);
|
||||
case OR -> modifyOr((OrExpression) expr);
|
||||
case PARAMETER -> modifyParameter((ParameterExpression) expr);
|
||||
case SELECT -> modifySelect((SelectExpression) expr);
|
||||
case TABLE -> modifyTable((TableExpression) expr);
|
||||
case TERNARY -> modifyTernary((TernaryExpression) expr);
|
||||
case UNARY -> modifyUnary((UnaryExpression) expr);
|
||||
case WHERE -> modifyWhere((WhereExpression) expr);
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
public Expression modifyAnd(AndExpression expr) {
|
||||
var exprs = new ArrayList<Expression>(expr.getExprs().size());
|
||||
for (Expression e : expr.getExprs()) {
|
||||
exprs.add(modify(e));
|
||||
}
|
||||
return new AndExpression(exprs);
|
||||
}
|
||||
|
||||
public Expression modifyBinary(BinaryExpression expr) {
|
||||
return new BinaryExpression(modify(expr.getLeft()), modify(expr.getRight()), expr.getOperator());
|
||||
}
|
||||
|
||||
public Expression modifyConstant(ConstantExpression expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Expression modifyField(FieldExpression expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Expression modifyOr(OrExpression expr) {
|
||||
var exprs = new ArrayList<Expression>(expr.getExprs().size());
|
||||
for (Expression e : expr.getExprs()) {
|
||||
exprs.add(modify(e));
|
||||
}
|
||||
return new OrExpression(exprs);
|
||||
}
|
||||
|
||||
public Expression modifyParameter(ParameterExpression expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Expression modifySelect(SelectExpression expr) {
|
||||
return new SelectExpression(expr.getFields(), modify(expr.getFrom()), expr.getFromAlias());
|
||||
}
|
||||
|
||||
public Expression modifyTable(TableExpression expr) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public Expression modifyTernary(TernaryExpression expr) {
|
||||
return new TernaryExpression(modify(expr.getCond()), modify(expr.getWhenTrue()), modify(expr.getWhenFalse()));
|
||||
}
|
||||
|
||||
public Expression modifyUnary(UnaryExpression expr) {
|
||||
return new UnaryExpression(modify(expr.getExpr()), expr.getOperator());
|
||||
}
|
||||
|
||||
public Expression modifyWhere(WhereExpression expr) {
|
||||
return new WhereExpression(modify(expr.getQueryable()), modify(expr.getWhere()));
|
||||
}
|
||||
}
|
||||
102
src/main/java/jef/expressions/modifier/ExpressionOptimizer.java
Normal file
102
src/main/java/jef/expressions/modifier/ExpressionOptimizer.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ExpressionOptimizer extends ExpressionModifier {
|
||||
@Override
|
||||
public Expression modifyAnd(AndExpression expr) {
|
||||
var ands = new ArrayList<Expression>(expr.getExprs().size() * 2);
|
||||
|
||||
//squash ands
|
||||
for (Expression e : expr.getExprs()) {
|
||||
if (e.getType() == Expression.Type.AND) {
|
||||
ands.addAll(((AndExpression) e).getExprs());
|
||||
} else {
|
||||
ands.add(e);
|
||||
}
|
||||
}
|
||||
ands.replaceAll(this::modify);
|
||||
|
||||
// x && false -> false
|
||||
for (Expression e : ands) {
|
||||
if (e == ConstantExpression.I0) {
|
||||
return ConstantExpression.I0;
|
||||
}
|
||||
}
|
||||
while (ands.remove(ConstantExpression.I1)) ;
|
||||
|
||||
return new AndExpression(ands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyOr(OrExpression expr) {
|
||||
var ors = new ArrayList<Expression>(expr.getExprs().size() * 2);
|
||||
|
||||
//squash ors
|
||||
for (Expression e : expr.getExprs()) {
|
||||
if (e.getType() == Expression.Type.OR) {
|
||||
ors.addAll(((OrExpression) e).getExprs());
|
||||
} else {
|
||||
ors.add(e);
|
||||
}
|
||||
}
|
||||
ors.replaceAll(this::modify);
|
||||
|
||||
// x || true -> true
|
||||
for (Expression e : ors) {
|
||||
if (e == ConstantExpression.I1) {
|
||||
return ConstantExpression.I1;
|
||||
}
|
||||
}
|
||||
while (ors.remove(ConstantExpression.I0)) ;
|
||||
|
||||
return new OrExpression(ors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyTernary(TernaryExpression expr) {
|
||||
if (expr.getWhenFalse() == ConstantExpression.I1 && expr.getWhenFalse() == ConstantExpression.I0) {
|
||||
//x ? 1 : 0 -> x
|
||||
return modify(expr.getCond());
|
||||
} else if (expr.getWhenFalse() == ConstantExpression.I0 && expr.getWhenFalse() == ConstantExpression.I1) {
|
||||
//x ? 0 : 1 -> !x
|
||||
return modify(new UnaryExpression(expr.getCond(), UnaryExpression.Operator.NOT));
|
||||
} else if (expr.getWhenFalse() == ConstantExpression.I0) {
|
||||
//x ? y : 0 -> x && y
|
||||
return modify(new AndExpression(expr.getCond(), expr.getWhenTrue()));
|
||||
} else if (expr.getWhenFalse() == ConstantExpression.I1) {
|
||||
//x ? y : 1 -> !x or y
|
||||
return modify(new OrExpression(new UnaryExpression(expr.getCond(), UnaryExpression.Operator.NOT), expr.getWhenTrue()));
|
||||
} else if (expr.getWhenTrue() instanceof TernaryExpression t && expr.getWhenFalse() == t.getWhenFalse()) {
|
||||
// x ? (y ? z : u) : u -> (x && y) ? z : u
|
||||
return new TernaryExpression(new AndExpression(expr.getCond(), t.getCond()), t.getWhenTrue(), t.getWhenFalse());
|
||||
} else {
|
||||
return super.modifyTernary(expr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyUnary(UnaryExpression expr) {
|
||||
if (expr.getExpr() instanceof UnaryExpression u
|
||||
&& expr.getOperator() == u.getOperator()
|
||||
&& expr.getOperator() == UnaryExpression.Operator.NOT) {
|
||||
//!!x -> x
|
||||
return modify(u.getExpr());
|
||||
} else if (expr.getExpr() instanceof BinaryExpression b
|
||||
&& expr.getOperator() == UnaryExpression.Operator.NOT
|
||||
&& b.getOperator().isInvertible()) {
|
||||
//!(a < b) -> a >= b
|
||||
return new BinaryExpression(b.getLeft(), b.getRight(), b.getOperator().invert());
|
||||
} else {
|
||||
return super.modifyUnary(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ExpressionOptimizerBottomUp extends ExpressionModifier {
|
||||
@Override
|
||||
public Expression modifyAnd(AndExpression expr) {
|
||||
var andsOpt = expr.getExprs().stream().map(this::modify).toList();
|
||||
var ands = new ArrayList<Expression>(expr.getExprs().size() * 2);
|
||||
|
||||
//squash ands
|
||||
for (Expression e : andsOpt) {
|
||||
if (e.getType() == Expression.Type.AND) {
|
||||
ands.addAll(((AndExpression) e).getExprs());
|
||||
} else {
|
||||
ands.add(e);
|
||||
}
|
||||
}
|
||||
ands.replaceAll(this::modify);
|
||||
|
||||
// x && false -> false
|
||||
for (Expression e : ands) {
|
||||
if (e == ConstantExpression.I0) {
|
||||
return ConstantExpression.I0;
|
||||
}
|
||||
}
|
||||
while (ands.remove(ConstantExpression.I1)) ;
|
||||
|
||||
return new AndExpression(ands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyOr(OrExpression expr) {
|
||||
var orsOpt = expr.getExprs().stream().map(this::modify).toList();
|
||||
var ors = new ArrayList<Expression>(expr.getExprs().size() * 2);
|
||||
|
||||
//squash ors
|
||||
for (Expression e : orsOpt) {
|
||||
if (e.getType() == Expression.Type.OR) {
|
||||
ors.addAll(((OrExpression) e).getExprs());
|
||||
} else {
|
||||
ors.add(e);
|
||||
}
|
||||
}
|
||||
ors.replaceAll(this::modify);
|
||||
|
||||
// x || true -> true
|
||||
for (Expression e : ors) {
|
||||
if (e == ConstantExpression.I1) {
|
||||
return ConstantExpression.I1;
|
||||
}
|
||||
}
|
||||
while (ors.remove(ConstantExpression.I0)) ;
|
||||
|
||||
return new OrExpression(ors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyTernary(TernaryExpression expr) {
|
||||
var cond = modify(expr.getCond());
|
||||
var whenTrue = modify(expr.getWhenTrue());
|
||||
var whenFalse = modify(expr.getWhenFalse());
|
||||
if (whenTrue == ConstantExpression.I1 && whenFalse == ConstantExpression.I0) {
|
||||
//x ? 1 : 0 -> x
|
||||
return cond;
|
||||
} else if (whenTrue == ConstantExpression.I0 && whenFalse == ConstantExpression.I1) {
|
||||
//x ? 0 : 1 -> !x
|
||||
return modify(new UnaryExpression(cond, UnaryExpression.Operator.NOT));
|
||||
} else if (whenFalse == ConstantExpression.I0) {
|
||||
//x ? y : 0 -> x && y
|
||||
return modify(new AndExpression(cond, whenTrue));
|
||||
} else if (whenFalse == ConstantExpression.I1) {
|
||||
//x ? y : 1 -> !x or y
|
||||
return modify(new OrExpression(new UnaryExpression(cond, UnaryExpression.Operator.NOT), whenTrue));
|
||||
} else if (whenTrue instanceof TernaryExpression t && whenFalse == t.getWhenFalse()) {
|
||||
// x ? (y ? z : u) : u -> (x && y) ? z : u
|
||||
return new TernaryExpression(new AndExpression(cond, t.getCond()), t.getWhenTrue(), t.getWhenFalse());
|
||||
} else {
|
||||
return super.modifyTernary(expr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyUnary(UnaryExpression expr) {
|
||||
var inner = modify(expr.getExpr());
|
||||
if (inner instanceof UnaryExpression u
|
||||
&& expr.getOperator() == u.getOperator()
|
||||
&& expr.getOperator() == UnaryExpression.Operator.NOT) {
|
||||
//!!x -> x
|
||||
return modify(u.getExpr());
|
||||
} else if (inner instanceof BinaryExpression b
|
||||
&& expr.getOperator() == UnaryExpression.Operator.NOT
|
||||
&& b.getOperator().isInvertible()) {
|
||||
//!(a < b) -> a >= b
|
||||
return new BinaryExpression(b.getLeft(), b.getRight(), b.getOperator().invert());
|
||||
} else {
|
||||
return super.modifyUnary(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.FieldExpression;
|
||||
|
||||
public class TableAliasInjector extends ExpressionModifier {
|
||||
private final String tableAlias;
|
||||
private final String prefix;
|
||||
|
||||
public TableAliasInjector(String tableAlias) {
|
||||
this.tableAlias = tableAlias;
|
||||
this.prefix = (tableAlias == null || tableAlias.isBlank()) ? "" : tableAlias + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression modifyField(FieldExpression expr) {
|
||||
return new FieldExpression(prefix + expr.getName());
|
||||
}
|
||||
}
|
||||
20
src/main/java/jef/expressions/modifier/TernaryRewriter.java
Normal file
20
src/main/java/jef/expressions/modifier/TernaryRewriter.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package jef.expressions.modifier;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class TernaryRewriter extends ExpressionModifier {
|
||||
@Override
|
||||
public Expression modifyTernary(TernaryExpression expr) {
|
||||
return new OrExpression(new AndExpression(expr.getCond(), expr.getWhenTrue()),
|
||||
new AndExpression(new UnaryExpression(expr.getCond(), UnaryExpression.Operator.NOT), expr.getWhenFalse()));
|
||||
// return new OrExpression(new AndExpression(expr.getCond(), expr.getWhenTrue()), expr.getWhenFalse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package jef.expressions.visitors;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.FieldExpression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.ParameterExpression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.TableExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
import jef.expressions.WhereExpression;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DebugExpressionVisitor extends ExpressionVisitor {
|
||||
private int indent = 0;
|
||||
|
||||
@Override
|
||||
public void visitAnd(AndExpression expr) {
|
||||
for (int i = 0; i < expr.getExprs().size(); i++) {
|
||||
indent++;
|
||||
visit(expr.getExprs().get(i));
|
||||
indent--;
|
||||
if (i + 1 < expr.getExprs().size())
|
||||
System.out.println(i() + "AND");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinary(BinaryExpression expr) {
|
||||
indent++;
|
||||
visit(expr.getLeft());
|
||||
indent--;
|
||||
System.out.println(i() + expr.getOperator());
|
||||
indent++;
|
||||
visit(expr.getRight());
|
||||
indent--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstant(ConstantExpression expr) {
|
||||
System.out.println(i() + expr.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitField(FieldExpression expr) {
|
||||
System.out.println(i() + expr.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOr(OrExpression expr) {
|
||||
for (int i = 0; i < expr.getExprs().size(); i++) {
|
||||
indent++;
|
||||
visit(expr.getExprs().get(i));
|
||||
indent--;
|
||||
if (i + 1 < expr.getExprs().size())
|
||||
System.out.println(i() + "OR");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParameter(ParameterExpression expr) {
|
||||
if (expr.getValue() instanceof Collection c) {
|
||||
System.out.println(i() + c.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
} else {
|
||||
System.out.println(i() + expr.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSelect(SelectExpression expr) {
|
||||
var table = expr.getFrom() instanceof TableExpression;
|
||||
var tableName = table ? ((TableExpression) expr.getFrom()).getName() : null;
|
||||
var tableAlias = (expr.getFromAlias() == null || expr.getFromAlias().isBlank()) ? "" : " " + expr.getFromAlias();
|
||||
System.out.println(i() + "SELECT " + expr.getFields().stream().collect(Collectors.joining(", "))
|
||||
+ " FROM" + (!table ? " (" : tableName + " " + tableAlias));
|
||||
if (!table) {
|
||||
indent++;
|
||||
visit(expr.getFrom());
|
||||
indent--;
|
||||
System.out.println(i() + ")" + tableAlias);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTable(TableExpression expr) {
|
||||
System.out.println(i() + expr.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTernary(TernaryExpression expr) {
|
||||
indent++;
|
||||
visit(expr.getCond());
|
||||
indent--;
|
||||
System.out.println(i() + "?");
|
||||
indent++;
|
||||
visit(expr.getWhenTrue());
|
||||
indent--;
|
||||
System.out.println(i() + ":");
|
||||
indent++;
|
||||
visit(expr.getWhenFalse());
|
||||
indent--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUnary(UnaryExpression expr) {
|
||||
System.out.println(i() + expr.getOperator());
|
||||
indent++;
|
||||
visit(expr.getExpr());
|
||||
indent--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitWhere(WhereExpression expr) {
|
||||
visit(expr.getQueryable());
|
||||
System.out.println(i() + "WHERE");
|
||||
indent++;
|
||||
visit(expr.getWhere());
|
||||
indent--;
|
||||
}
|
||||
|
||||
private String i() {
|
||||
return " ".repeat(indent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package jef.expressions.visitors;
|
||||
|
||||
import jef.expressions.AndExpression;
|
||||
import jef.expressions.BinaryExpression;
|
||||
import jef.expressions.ConstantExpression;
|
||||
import jef.expressions.Expression;
|
||||
import jef.expressions.FieldExpression;
|
||||
import jef.expressions.OrExpression;
|
||||
import jef.expressions.ParameterExpression;
|
||||
import jef.expressions.SelectExpression;
|
||||
import jef.expressions.TableExpression;
|
||||
import jef.expressions.TernaryExpression;
|
||||
import jef.expressions.UnaryExpression;
|
||||
import jef.expressions.WhereExpression;
|
||||
|
||||
public abstract class ExpressionVisitor {
|
||||
public void visit(Expression expr) {
|
||||
switch (expr.getType()) {
|
||||
case AND -> visitAnd((AndExpression) expr);
|
||||
case BINARY -> visitBinary((BinaryExpression) expr);
|
||||
case CONSTANT -> visitConstant((ConstantExpression) expr);
|
||||
case FIELD -> visitField((FieldExpression) expr);
|
||||
case OR -> visitOr((OrExpression) expr);
|
||||
case PARAMETER -> visitParameter((ParameterExpression) expr);
|
||||
case SELECT -> visitSelect((SelectExpression) expr);
|
||||
case TABLE -> visitTable((TableExpression) expr);
|
||||
case TERNARY -> visitTernary((TernaryExpression) expr);
|
||||
case UNARY -> visitUnary((UnaryExpression) expr);
|
||||
case WHERE -> visitWhere((WhereExpression) expr);
|
||||
default -> throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public void visitAnd(AndExpression expr) {
|
||||
for (Expression e : expr.getExprs()) {
|
||||
visit(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitBinary(BinaryExpression expr) {
|
||||
visit(expr.getLeft());
|
||||
visit(expr.getRight());
|
||||
}
|
||||
|
||||
public void visitConstant(ConstantExpression expr) {
|
||||
}
|
||||
|
||||
public void visitField(FieldExpression expr) {
|
||||
}
|
||||
|
||||
public void visitOr(OrExpression expr) {
|
||||
for (Expression e : expr.getExprs()) {
|
||||
visit(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitParameter(ParameterExpression expr) {
|
||||
}
|
||||
|
||||
public void visitSelect(SelectExpression expr) {
|
||||
visit(expr.getFrom());
|
||||
}
|
||||
|
||||
public void visitTable(TableExpression expr) {
|
||||
}
|
||||
|
||||
public void visitTernary(TernaryExpression expr) {
|
||||
visit(expr.getCond());
|
||||
visit(expr.getWhenTrue());
|
||||
visit(expr.getWhenFalse());
|
||||
}
|
||||
|
||||
public void visitUnary(UnaryExpression expr) {
|
||||
visit(expr.getExpr());
|
||||
}
|
||||
|
||||
public void visitWhere(WhereExpression expr) {
|
||||
visit(expr.getQueryable());
|
||||
visit(expr.getWhere());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user