style(muscript-optimizer): split out optimization of inner expressions to permit approach based exclusively on pattern matching
This commit is contained in:
parent
a23af078e1
commit
130e246ac2
@ -48,7 +48,7 @@ public class ExprUtils {
|
||||
public static StringExpr asString(Expr expression) {
|
||||
return switch (unpack(expression)) {
|
||||
case StringExpr string -> string;
|
||||
case NumberLiteral number -> new StringLiteral(expression.location(), StringFormatter.toString(number.value()));
|
||||
case NumberLiteral number -> new StringLiteral(expression.location(), StringFormatter.toStringPrecise(number.value()));
|
||||
case BoolLiteral bool -> new StringLiteral(expression.location(), bool.value() ? "true" : "false");
|
||||
case NullLiteral nl -> new StringLiteral(expression.location(), "null");
|
||||
case DynamicConditional(var location, var condition, var ifTrue, var ifFalse) -> new StringConditional(location, condition, asString(ifTrue), asString(ifFalse));
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.gitlab.jfronny.muscript.optimizer;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.ast.*;
|
||||
import io.gitlab.jfronny.muscript.ast.bool.*;
|
||||
import io.gitlab.jfronny.muscript.ast.context.Script;
|
||||
@ -12,7 +11,6 @@ import io.gitlab.jfronny.muscript.core.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -39,328 +37,140 @@ public class Optimizer {
|
||||
}
|
||||
|
||||
public static StringExpr optimize(StringExpr expr) {
|
||||
return switch (expr) {
|
||||
return switch (optimizeInner(expr)) {
|
||||
case null -> throw new NullPointerException();
|
||||
case StringConditional(var location, BoolLiteral(var location1, var value), var ifTrue, var ifFalse) -> value ? ifTrue : ifFalse;
|
||||
case StringConditional(var location, Not(var location1, var condition), var ifTrue, var ifFalse) -> new StringConditional(location, condition, ifFalse, ifTrue);
|
||||
case Concatenate(var location, StringLiteral(var location1, var left), StringLiteral(var location2, var right)) -> literal(location, left + right);
|
||||
case Concatenate(var location, Concatenate(var location1, var left1, StringLiteral(var location2, var right1)), StringLiteral(var location3, var right2)) -> new Concatenate(location1, left1, literal(location2, right1 + right2));
|
||||
case Concatenate(var location, StringLiteral(var location1, var left1), Concatenate(var location2, StringLiteral(var location3, var left2), var right)) -> new Concatenate(location2, literal(location3, left1 + left2), right);
|
||||
case StringExpr fallback -> fallback; // Used instead of default to still keep applied optimizations
|
||||
};
|
||||
}
|
||||
|
||||
private static StringExpr optimizeInner(StringExpr expr) {
|
||||
return switch (expr) {
|
||||
case null -> null;
|
||||
case StringLiteral e -> e;
|
||||
case ExtensibleStringExpr e -> e.optimize();
|
||||
case StringUnpack(var inner) -> asString(unpack(optimize(inner)));
|
||||
case StringCoerce(var inner) -> switch (unpack(inner)) {
|
||||
case NullLiteral e -> new StringLiteral(inner.location(), "null");
|
||||
case BoolLiteral(var location, var value) -> new StringLiteral(location, value ? "true" : "false");
|
||||
case NumberLiteral(var location, var value) -> new StringLiteral(location, StringFormatter.toStringPrecise(value));
|
||||
case StringExpr e -> e;
|
||||
case DynamicLiteral(var location, var value) -> new StringLiteral(location, StringFormatter.toString(value));
|
||||
default -> new StringCoerce(inner);
|
||||
};
|
||||
case StringCoerce(var inner) -> asString(unpack(optimize(inner)));
|
||||
case StringAssign(var location, var variable, var value) -> new StringAssign(location, variable, optimize(value));
|
||||
case StringConditional e -> {
|
||||
var condition = optimize(e.condition());
|
||||
var ifTrue = optimize(e.ifTrue());
|
||||
var ifFalse = optimize(e.ifFalse());
|
||||
if (condition instanceof BoolLiteral(var location, var value)) yield value ? ifTrue : ifFalse;
|
||||
if (ifTrue.equals(ifFalse)) yield ifTrue;
|
||||
if (condition instanceof Not(var location, var inner)) yield new StringConditional(e.location(), inner, ifFalse, ifTrue);
|
||||
yield new StringConditional(e.location(), condition, ifTrue, ifFalse);
|
||||
}
|
||||
case Concatenate e -> {
|
||||
var location = e.location();
|
||||
var left = optimize(e.left());
|
||||
var right = optimize(e.right());
|
||||
if (left instanceof StringLiteral(var location1, var leftValue)
|
||||
&& right instanceof StringLiteral(var location2, var rightValue))
|
||||
yield literal(location, leftValue + rightValue);
|
||||
if (right instanceof StringLiteral(var location1, var rightValue)
|
||||
&& left instanceof Concatenate(var location2, var newLeft, StringLiteral(var location3, var leftValue))) {
|
||||
yield new Concatenate(location, newLeft, literal(location2, leftValue + rightValue));
|
||||
}
|
||||
if (left instanceof StringLiteral(var location1, var leftValue)
|
||||
&& right instanceof Concatenate(var location2, StringLiteral(var location3, var rightValue), var newRight)) {
|
||||
yield new Concatenate(location, literal(location2, leftValue + rightValue), newRight);
|
||||
}
|
||||
yield new Concatenate(location, left, right);
|
||||
}
|
||||
case StringConditional(var location, var condition, var ifTrue, var ifFalse) -> new StringConditional(location, optimize(condition), optimize(ifTrue), optimize(ifFalse));
|
||||
case Concatenate(var location, var left, var right) -> new Concatenate(location, optimize(left), optimize(right));
|
||||
};
|
||||
}
|
||||
|
||||
public static NumberExpr optimize(NumberExpr expr) {
|
||||
return switch (expr) {
|
||||
return switch (optimizeInner(expr)) {
|
||||
case null -> throw new NullPointerException();
|
||||
case NumberConditional(var location, BoolLiteral(var location1, var value), var ifTrue, var ifFalse) -> value ? ifTrue : ifFalse;
|
||||
case NumberConditional(var location, Not(var location1, var condition), var ifTrue, var ifFalse) -> new NumberConditional(location, condition, ifFalse, ifTrue);
|
||||
case Add(var location, NumberLiteral(var location1, var augend), NumberLiteral(var location2, var addend)) -> literal(location, augend + addend);
|
||||
case Add(var location, Negate(var location1, var augend), Negate(var location2, var addend)) -> new Negate(location, optimize(new Add(location, augend, addend)));
|
||||
case Add(var location, Negate(var location1, var augend), var addend) -> optimize(new Subtract(location, addend, augend));
|
||||
case Add(var location, var augend, Negate(var location1, var addend)) -> optimize(new Subtract(location, augend, addend));
|
||||
case Subtract(var location, NumberLiteral(var location1, var minuend), NumberLiteral(var location2, var subtrahend)) -> literal(location, minuend - subtrahend);
|
||||
case Subtract(var location, Subtract(var location1, var minuend1, var subtrahend1), var subtrahend) -> optimize(new Subtract(location, minuend1, new Add(location1, subtrahend1, subtrahend)));
|
||||
case Subtract(var location, var minuend, Subtract(var location1, var minuend1, var subtrahend1)) -> new Subtract(location, new Add(location1, minuend, subtrahend1), minuend1);
|
||||
case Subtract(var location, Negate(var location1, var minuend), Negate(var location2, var subtrahend)) -> new Subtract(location, subtrahend, minuend);
|
||||
case Subtract(var location, Negate(var location1, var minuend), var subtrahend) -> optimize(new Negate(location, new Add(location, minuend, subtrahend)));
|
||||
case Subtract(var location, var minuend, Negate(var location1, var subtrahend)) -> optimize(new Add(location, minuend, subtrahend));
|
||||
case Negate(var location, NumberLiteral(var location1, var value)) -> literal(location, -value);
|
||||
case Negate(var location, Negate(var location1, var inner)) -> inner;
|
||||
case Negate(var location, Subtract(var location1, var minuend, var subtrahend)) -> optimize(new Subtract(location, subtrahend, minuend));
|
||||
case Multiply(var location, NumberLiteral(var location1, var multiplier), NumberLiteral(var location2, var multiplicand)) -> literal(location, multiplier * multiplicand);
|
||||
//TODO optimize multiplication with or division by 1
|
||||
case Divide(var location, NumberLiteral(var location1, var dividend), NumberLiteral(var location2, var divisor)) -> literal(location, dividend / divisor);
|
||||
case Divide(var location, Divide(var location1, var dividend1, var divisor1), var divisor) -> new Divide(location, dividend1, new Multiply(location1, divisor1, divisor));
|
||||
case Divide(var location, var dividend, Divide(var location1, var dividend1, var divisor1)) -> new Divide(location, new Multiply(location1, dividend, divisor1), dividend1);
|
||||
case Divide(var location, Negate(var location1, var dividend), Negate(var location2, var divisor)) -> new Divide(location, divisor, dividend);
|
||||
case Divide(var location, Negate(var location1, var dividend), var divisor) -> new Negate(location, new Divide(location1, dividend, divisor));
|
||||
case Divide(var location, var dividend, Negate(var location1, var divisor)) -> new Negate(location, new Divide(location1, dividend, divisor));
|
||||
case Modulo(var location, NumberLiteral(var location1, var dividend), NumberLiteral(var location2, var divisor)) -> literal(location, dividend % divisor);
|
||||
case Power(var location, NumberLiteral(var location1, var base), NumberLiteral(var location2, var exponent)) -> literal(location, Math.pow(base, exponent));
|
||||
case Power(var location, Multiply(var location1, NumberLiteral(var location2, var multiplier), var multiplicand), NumberLiteral(var location3, var exponent)) -> new Multiply(location1, literal(location, Math.pow(multiplier, exponent)), new Power(location, multiplicand, literal(location3, exponent)));
|
||||
case Power(var location, Multiply(var location1, var multiplier, NumberLiteral(var location2, var multiplicand)), NumberLiteral(var location3, var exponent)) -> new Multiply(location1, literal(location, Math.pow(multiplicand, exponent)), new Power(location, multiplier, literal(location3, exponent)));
|
||||
case NumberExpr fallback -> fallback; // Used instead of default to still keep applied optimizations
|
||||
};
|
||||
}
|
||||
|
||||
private static NumberExpr optimizeInner(NumberExpr expr) {
|
||||
return switch (expr) {
|
||||
case null -> null;
|
||||
case NumberLiteral e -> e;
|
||||
case ExtensibleNumberExpr e -> e.optimize();
|
||||
case NumberUnpack(var inner) -> asNumber(unpack(optimize(inner)));
|
||||
case NumberAssign(var location, var variable, var value) -> new NumberAssign(location, variable, optimize(value));
|
||||
case NumberConditional e -> {
|
||||
var condition = optimize(e.condition());
|
||||
var ifTrue = optimize(e.ifTrue());
|
||||
var ifFalse = optimize(e.ifFalse());
|
||||
if (condition instanceof BoolLiteral(var location, var value))
|
||||
yield value ? ifTrue : ifFalse;
|
||||
if (ifTrue.equals(ifFalse)) yield ifTrue;
|
||||
if (condition instanceof Not(var location, var inner))
|
||||
yield new NumberConditional(e.location(), inner, ifFalse, ifTrue);
|
||||
yield new NumberConditional(e.location(), condition, ifTrue, ifFalse);
|
||||
}
|
||||
case Add e -> {
|
||||
var location = e.location();
|
||||
var augend = optimize(e.augend());
|
||||
var addend = optimize(e.addend());
|
||||
if (augend instanceof NumberLiteral(var location1, var augendValue)
|
||||
&& addend instanceof NumberLiteral(var location2, var addendValue))
|
||||
yield literal(location, augendValue + addendValue);
|
||||
if (augend instanceof Negate(var location1, var innerAugend)
|
||||
&& addend instanceof Negate(var location2, var innerAddend))
|
||||
yield new Negate(location, optimize(new Add(location, innerAugend, innerAddend)));
|
||||
if (augend instanceof Negate(var location1, var innerAugend))
|
||||
yield optimize(new Subtract(location, addend, innerAugend));
|
||||
if (addend instanceof Negate(var location1, var innerAddend))
|
||||
yield optimize(new Subtract(location, augend, innerAddend));
|
||||
yield new Add(location, augend, addend);
|
||||
}
|
||||
case Subtract e -> {
|
||||
var location = e.location();
|
||||
var minuend = optimize(e.minuend());
|
||||
var subtrahend = optimize(e.subtrahend());
|
||||
if (minuend instanceof NumberLiteral(var location1, var minuendValue)
|
||||
&& subtrahend instanceof NumberLiteral(var location2, var subtrahendValue))
|
||||
yield literal(location, minuendValue - subtrahendValue);
|
||||
if (minuend instanceof Subtract(var location1, var minuend1, var subtrahend1))
|
||||
yield optimize(new Subtract(location, minuend1, new Add(minuend.location(), subtrahend1, subtrahend)));
|
||||
if (subtrahend instanceof Subtract(var location1, var minuend1, var subtrahend1))
|
||||
yield new Subtract(location, new Add(subtrahend.location(), minuend, subtrahend1), minuend1);
|
||||
if (subtrahend instanceof Negate(var location1, var inner)) {
|
||||
if (minuend instanceof Negate(var location2, var inner2))
|
||||
yield new Subtract(location, inner, inner2);
|
||||
yield optimize(new Add(location, minuend, inner));
|
||||
}
|
||||
if (minuend instanceof Negate(var location1, var inner)) {
|
||||
yield optimize(new Negate(location, new Add(location, inner, subtrahend)));
|
||||
}
|
||||
yield new Subtract(location, minuend, subtrahend);
|
||||
}
|
||||
case Negate e -> {
|
||||
var location = e.location();
|
||||
var inner = optimize(e.inner());
|
||||
if (inner instanceof Negate(var location1, var innerInner))
|
||||
yield innerInner;
|
||||
if (inner instanceof NumberLiteral(var location1, var value))
|
||||
yield literal(location, -value);
|
||||
if (inner instanceof Subtract(var location1, var minuend, var subtrahend))
|
||||
yield optimize(new Subtract(location, subtrahend, minuend));
|
||||
yield new Negate(location, inner);
|
||||
}
|
||||
case Multiply e -> {
|
||||
var location = e.location();
|
||||
var multiplier = optimize(e.multiplier());
|
||||
var multiplicand = optimize(e.multiplicand());
|
||||
if (multiplier instanceof NumberLiteral(var location1, var multiplierValue)
|
||||
&& multiplicand instanceof NumberLiteral(var location2, var multiplicandValue))
|
||||
yield literal(location, multiplierValue * multiplicandValue);
|
||||
yield new Multiply(location, multiplicand, multiplier);
|
||||
}
|
||||
case Divide e -> {
|
||||
var location = e.location();
|
||||
var dividend = optimize(e.dividend());
|
||||
var divisor = optimize(e.divisor());
|
||||
if (dividend instanceof NumberLiteral(var location1, var dividendValue)
|
||||
&& divisor instanceof NumberLiteral(var location2, var divisorValue))
|
||||
yield literal(location, dividendValue / divisorValue);
|
||||
if (dividend instanceof Divide(var location1, var dividend1, var divisor1))
|
||||
yield new Divide(location, dividend1, new Multiply(dividend.location(), divisor1, divisor));
|
||||
if (divisor instanceof Divide(var location1, var dividend1, var divisor1))
|
||||
yield new Divide(location, new Multiply(dividend.location(), dividend, divisor1), dividend1);
|
||||
yield new Divide(location, dividend, divisor);
|
||||
}
|
||||
case Modulo e -> {
|
||||
var location = e.location();
|
||||
var dividend = optimize(e.dividend());
|
||||
var divisor = optimize(e.divisor());
|
||||
if (dividend instanceof NumberLiteral(var location1, var dividendValue)
|
||||
&& divisor instanceof NumberLiteral(var location2, var divisorValue))
|
||||
yield literal(location, dividendValue % divisorValue);
|
||||
yield new Modulo(location, dividend, divisor);
|
||||
}
|
||||
case Power e -> {
|
||||
var location = e.location();
|
||||
var base = optimize(e.base());
|
||||
var exponent = optimize(e.exponent());
|
||||
if (base instanceof NumberLiteral(var location1, var baseValue)
|
||||
&& exponent instanceof NumberLiteral(var location2, var exponentValue))
|
||||
yield literal(location, Math.pow(baseValue, exponentValue));
|
||||
if (exponent instanceof NumberLiteral(var location1, var exp)
|
||||
&& base instanceof Multiply(var location2, var multiplier, var multiplicand)) {
|
||||
if (multiplier instanceof NumberLiteral(var location3, var value))
|
||||
yield new Multiply(location2,
|
||||
literal(location, Math.pow(value, exp)),
|
||||
new Power(location, multiplicand, literal(exp)));
|
||||
if (multiplicand instanceof NumberLiteral(var location3, var value))
|
||||
yield new Multiply(location2,
|
||||
literal(location, Math.pow(value, exp)),
|
||||
new Power(location, multiplier, literal(exp)));
|
||||
}
|
||||
yield new Power(location, base, exponent);
|
||||
}
|
||||
case NumberConditional(var location, var condition, var ifTrue, var ifFalse) -> new NumberConditional(location, optimize(condition), optimize(ifTrue), optimize(ifFalse));
|
||||
case Add(var location, var augend, var addend) -> new Add(location, optimize(augend), optimize(addend));
|
||||
case Subtract(var location, var minuend, var subtrahend) -> new Subtract(location, optimize(minuend), optimize(subtrahend));
|
||||
case Negate(var location, var inner) -> new Negate(location, optimize(inner));
|
||||
case Multiply(var location, var multiplier, var multiplicand) -> new Multiply(location, optimize(multiplier), optimize(multiplicand));
|
||||
case Divide(var location, var dividend, var divisor) -> new Divide(location, optimize(dividend), optimize(divisor));
|
||||
case Modulo(var location, var dividend, var divisor) -> new Modulo(location, optimize(dividend), optimize(divisor));
|
||||
case Power(var location, var base, var exponent) -> new Power(location, optimize(base), optimize(exponent));
|
||||
};
|
||||
}
|
||||
|
||||
public static BoolExpr optimize(BoolExpr expr) {
|
||||
return switch (expr) {
|
||||
return switch (optimizeInner(expr)) {
|
||||
case null -> throw new NullPointerException();
|
||||
case BoolConditional(var location, BoolLiteral(var location1, var value), var ifTrue, var ifFalse) -> value ? ifTrue : ifFalse;
|
||||
case BoolConditional(var location, Not(var location1, var condition), var ifTrue, var ifFalse) -> new BoolConditional(location, condition, ifFalse, ifTrue);
|
||||
case And(var location, BoolLiteral(var location1, var left), var right) -> left ? right : literal(location, false);
|
||||
case And(var location, var left, BoolLiteral(var location1, var right)) -> right ? left : literal(location, false);
|
||||
case Or(var location, BoolLiteral(var location1, var left), var right) -> left ? literal(location, true) : right;
|
||||
case Or(var location, var left, BoolLiteral(var location1, var right)) -> right ? literal(location, true) : left;
|
||||
case Not(var location, BoolLiteral(var location1, var value)) -> literal(location, !value);
|
||||
case Not(var location, Not(var location1, var inner)) -> inner;
|
||||
case GreaterThan(var location, NumberLiteral(var location1, var left), NumberLiteral(var location2, var right)) -> literal(location, left > right);
|
||||
case GreaterThan(var location, Divide(var location1, var dividend, var divisor), var right) -> optimize(new GreaterThan(location, dividend, new Multiply(location1, right, divisor)));
|
||||
case GreaterThan(var location, Negate(var location1, var inner), var right) -> optimize(new GreaterThan(location, new Negate(right.location(), right), inner));
|
||||
case GreaterThan(var location, Subtract(var location1, var minuend, var subtrahend), var right) -> optimize(new GreaterThan(location, minuend, new Add(location1, subtrahend, right)));
|
||||
// Modulo is left out because it is too complicated for this naive impl
|
||||
// Multiply is left out since it would transform into a division and may be 0
|
||||
case GreaterThan(var location, Add(var location1, var augend, var addend), var right) -> optimize(new GreaterThan(location, augend, new Subtract(location1, right, addend)));
|
||||
// Power is left out because it can't be transformed cleanly either
|
||||
case BoolExpr fallback -> fallback; // Used instead of default to still keep applied optimizations
|
||||
};
|
||||
}
|
||||
|
||||
private static BoolExpr optimizeInner(BoolExpr expr) {
|
||||
return switch (expr) {
|
||||
case null -> null;
|
||||
case BoolLiteral e -> e;
|
||||
case ExtensibleBoolExpr e -> e.optimize();
|
||||
case BoolUnpack(var inner) -> asBool(unpack(optimize(inner)));
|
||||
case BoolAssign(var location, var variable, var value) -> new BoolAssign(location, variable, optimize(value));
|
||||
case BoolConditional e -> {
|
||||
var condition = optimize(e.condition());
|
||||
var ifTrue = optimize(e.ifTrue());
|
||||
var ifFalse = optimize(e.ifFalse());
|
||||
if (condition instanceof BoolLiteral(var location, var value))
|
||||
yield value ? ifTrue : ifFalse;
|
||||
if (ifTrue.equals(ifFalse)) yield ifTrue;
|
||||
if (condition instanceof Not(var location, var inner))
|
||||
yield new BoolConditional(e.location(), inner, ifFalse, ifTrue);
|
||||
yield new BoolConditional(e.location(), condition, ifTrue, ifFalse);
|
||||
}
|
||||
case And e -> {
|
||||
var location = e.location();
|
||||
var left = optimize(e.left());
|
||||
var right = optimize(e.right());
|
||||
if (left instanceof BoolLiteral(var location1, var leftValue))
|
||||
yield leftValue ? right : literal(location, false);
|
||||
if (right instanceof BoolLiteral(var location1, var rightValue))
|
||||
yield rightValue ? left : literal(location, false);
|
||||
yield new And(location, left, right);
|
||||
}
|
||||
case Or e -> {
|
||||
var location = e.location();
|
||||
var left = optimize(e.left());
|
||||
var right = optimize(e.right());
|
||||
if (left instanceof BoolLiteral(var location1, var leftValue))
|
||||
yield leftValue ? literal(location, true) : right;
|
||||
if (right instanceof BoolLiteral(var location1, var rightValue))
|
||||
yield rightValue ? literal(location, true) : left;
|
||||
yield new Or(location, left, right);
|
||||
}
|
||||
case Not e -> {
|
||||
var location = e.location();
|
||||
var inner = optimize(e.inner());
|
||||
if (inner instanceof BoolLiteral(var location1, var value))
|
||||
yield literal(location, !value);
|
||||
if (inner instanceof Not(var location1, var innerInner))
|
||||
yield innerInner;
|
||||
yield new Not(location, inner);
|
||||
}
|
||||
case Equals e -> {
|
||||
var location = e.location();
|
||||
var left = optimize(e.left());
|
||||
var right = optimize(e.right());
|
||||
if (left.equals(right))
|
||||
yield literal(location, true);
|
||||
yield new Equals(location, left, right);
|
||||
}
|
||||
case GreaterThan e -> {
|
||||
var location = e.location();
|
||||
var left = optimize(e.left());
|
||||
var right = optimize(e.right());
|
||||
if (left instanceof NumberLiteral(var location1, var leftValue)
|
||||
&& right instanceof NumberLiteral(var location2, var rightValue))
|
||||
yield literal(location, leftValue > rightValue);
|
||||
if (left instanceof Divide(var location1, var dividend, var divisor))
|
||||
yield optimize(new GreaterThan(location, dividend, new Multiply(location1, right, divisor)));
|
||||
if (left instanceof Negate(var location1, var inner))
|
||||
yield optimize(new GreaterThan(location, new Negate(right.location(), right), inner));
|
||||
if (left instanceof Subtract(var location1, var minuend, var subtrahend))
|
||||
yield optimize(new GreaterThan(location, minuend, new Add(location1, subtrahend, right)));
|
||||
// Modulo is left out because it is too complicated for this naive impl
|
||||
// Multiply is left out since it would transform into a division and may be 0
|
||||
if (left instanceof Add(var location1, var augend, var addend))
|
||||
yield optimize(new GreaterThan(location, augend, new Subtract(location1, addend, right)));
|
||||
// Power is left out because it can't be transformed cleanly either
|
||||
yield new GreaterThan(location, left, right);
|
||||
}
|
||||
case BoolConditional(var location, var condition, var ifTrue, var ifFalse) -> new BoolConditional(location, optimize(condition), optimize(ifTrue), optimize(ifFalse));
|
||||
case And(var location, var left, var right) -> new And(location, optimize(left), optimize(right));
|
||||
case Or(var location, var left, var right) -> new Or(location, optimize(left), optimize(right));
|
||||
case Not(var location, var inner) -> new Not(location, optimize(inner));
|
||||
case Equals(var location, var left, var right) -> new Equals(location, optimize(left), optimize(right));
|
||||
case GreaterThan(var location, var left, var right) -> new GreaterThan(location, optimize(left), optimize(right));
|
||||
};
|
||||
}
|
||||
|
||||
public static DynamicExpr optimize(DynamicExpr expr) {
|
||||
return switch (expr) {
|
||||
return switch (optimizeInner(expr)) {
|
||||
case null -> throw new NullPointerException();
|
||||
case DynamicLiteral e -> e;
|
||||
case ExtensibleDynamicExpr e -> e.optimize();
|
||||
case DynamicCoerce e -> asDynamic(unpack(optimize(e.inner())));
|
||||
case DynamicAssign(var location, var variable, var value) -> new DynamicAssign(location, variable, optimize(value));
|
||||
case DynamicConditional e -> {
|
||||
var condition = optimize(e.condition());
|
||||
var ifTrue = optimize(e.ifTrue());
|
||||
var ifFalse = optimize(e.ifFalse());
|
||||
if (condition instanceof BoolLiteral(var location, var value))
|
||||
yield value ? ifTrue : ifFalse;
|
||||
if (ifTrue.equals(ifFalse)) yield ifTrue;
|
||||
if (condition instanceof Not(var location, var inner))
|
||||
yield new DynamicConditional(e.location(), inner, ifFalse, ifTrue);
|
||||
yield new DynamicConditional(e.location(), condition, ifTrue, ifFalse);
|
||||
}
|
||||
case This e -> e;
|
||||
case Variable e -> e;
|
||||
case Get(var location, var left, var name) -> new Get(location, optimize(left), optimize(name));
|
||||
case At(var location, var left, var index) -> new At(location, optimize(left), optimize(index));
|
||||
case GetOrAt(var location, var leftX, var nameOrIndexX) -> {
|
||||
var left = optimize(leftX);
|
||||
var nameOrIndex = optimize(nameOrIndexX);
|
||||
if (nameOrIndex instanceof StringExpr name) yield new Get(location, left, name);
|
||||
if (nameOrIndex instanceof NumberExpr index) yield new At(location, left, index);
|
||||
if (left instanceof ObjectLiteral l) yield new Get(location, optimize(l), asString(nameOrIndex));
|
||||
if (left instanceof ListLiteral l) yield new At(location, optimize(l), asNumber(nameOrIndex));
|
||||
yield new GetOrAt(location, optimize(left), optimize(nameOrIndex));
|
||||
}
|
||||
case Bind(var location, var callable, var parameter) -> new Bind(location, optimize(callable), optimize(parameter));
|
||||
case Call e -> {
|
||||
var location = e.location();
|
||||
var callable = optimize(e.callable());
|
||||
var args = new ArrayList<Call.Argument>();
|
||||
if (callable instanceof Bind(var location1, var callable1, var parameter1)) {
|
||||
callable = callable1;
|
||||
args.add(new Call.Argument(parameter1, false));
|
||||
}
|
||||
for (Call.Argument arg : e.arguments())
|
||||
args.add(new Call.Argument(optimize(arg.value()), arg.variadic()));
|
||||
if (callable instanceof Closure(var location1, var boundArgs, var variadic, var steps, var finish)) {
|
||||
yield new ExprGroup(
|
||||
location1,
|
||||
Stream.concat(steps.stream(), Stream.of(finish)).toList(),
|
||||
new ExprGroup.PackedArgs(args, boundArgs, variadic),
|
||||
true
|
||||
);
|
||||
}
|
||||
yield new Call(location, callable, args);
|
||||
}
|
||||
case Closure(var location, var boundArgs, var variadic, var steps, var finish) -> new Closure(
|
||||
location,
|
||||
boundArgs,
|
||||
variadic,
|
||||
steps.stream()
|
||||
.map(Optimizer::optimize)
|
||||
.flatMap(Optimizer::extractSideEffects)
|
||||
.map(Optimizer::optimize)
|
||||
.toList(),
|
||||
optimize(finish));
|
||||
case ExprGroup(var location, var steps, var finish, var packedArgs, var fork) -> {
|
||||
List<Expr> exprs = Stream.concat(
|
||||
steps.stream()
|
||||
.map(Optimizer::optimize)
|
||||
.flatMap(Optimizer::extractSideEffects)
|
||||
.map(Optimizer::optimize),
|
||||
Stream.of(optimize(finish))
|
||||
).toList();
|
||||
yield packedArgs == null
|
||||
? asDynamic(ExprGroup.of(location, exprs, fork))
|
||||
: new ExprGroup(location, exprs, packedArgs, fork);
|
||||
}
|
||||
case ListLiteral(var location, var elements) -> new ListLiteral(location, elements.stream().map(Optimizer::optimize).toList());
|
||||
case ObjectLiteral e -> {
|
||||
var location = e.location();
|
||||
case DynamicConditional(var location, BoolLiteral(var location1, var value), var ifTrue, var ifFalse) -> value ? ifTrue : ifFalse;
|
||||
case DynamicConditional(var location, Not(var location1, var inner), var ifTrue, var ifFalse) -> optimize(new DynamicConditional(location, inner, ifFalse, ifTrue));
|
||||
case DynamicConditional(var location, var condition, var ifTrue, var ifFalse) -> ifTrue.equals(ifFalse)
|
||||
? new ExprGroup(location, extractSideEffects(condition).map(Optimizer::optimize).toList(), ifTrue, null, false)
|
||||
: new DynamicConditional(location, condition, ifTrue, ifFalse);
|
||||
case GetOrAt(var location, ObjectLiteral l, var nameOrIndex) -> new Get(location, l, asString(nameOrIndex));
|
||||
case GetOrAt(var location, ListLiteral l, var nameOrIndex) -> new At(location, l, asNumber(nameOrIndex));
|
||||
case GetOrAt(var location, var left, StringExpr name) -> new Get(location, left, name);
|
||||
case GetOrAt(var location, var left, NumberExpr index) -> new At(location, left, index);
|
||||
case Call(var location, Bind(var location1, var callable, var parameter), var arguments) -> optimize(new Call(location, callable, concat(arguments, new Call.Argument(parameter, false))));
|
||||
case Call(var location, Closure(var location1, var boundArgs, var variadic, var steps, var finish), var arguments) -> new ExprGroup(location1, concat(steps, finish), new ExprGroup.PackedArgs(arguments, boundArgs, variadic), true);
|
||||
case ObjectLiteral(var location, var originalContent) -> {
|
||||
var content = new LinkedHashMap<String, DynamicExpr>();
|
||||
var literalContent = new LinkedHashMap<String, Dynamic>();
|
||||
boolean literal = true;
|
||||
for (Map.Entry<String, DynamicExpr> entry : e.content().entrySet()) {
|
||||
for (Map.Entry<String, DynamicExpr> entry : originalContent.entrySet()) {
|
||||
DynamicExpr de = optimize(entry.getValue());
|
||||
if (de instanceof DynamicLiteral(var location1, var cnt) && literal) {
|
||||
if (cnt instanceof Dynamic d) literalContent.put(entry.getKey(), d);
|
||||
@ -371,9 +181,45 @@ public class Optimizer {
|
||||
if (literal) yield new DynamicLiteral(location, DFinal.of(literalContent));
|
||||
yield new ObjectLiteral(location, content);
|
||||
}
|
||||
case DynamicExpr fallback -> fallback; // Used instead of default to still keep applied optimizations
|
||||
};
|
||||
}
|
||||
|
||||
private static DynamicExpr optimizeInner(DynamicExpr expr) {
|
||||
return switch (expr) {
|
||||
case null -> null;
|
||||
case DynamicLiteral e -> e;
|
||||
case ExtensibleDynamicExpr e -> e.optimize();
|
||||
case DynamicCoerce(var inner) -> asDynamic(unpack(optimize(inner)));
|
||||
case DynamicAssign(var location, var variable, var value) -> new DynamicAssign(location, variable, optimize(value));
|
||||
case DynamicConditional(var location, var condition, var ifTrue, var ifFalse) -> new DynamicConditional(location, optimize(condition), optimize(ifTrue), optimize(ifFalse));
|
||||
case This e -> e;
|
||||
case Variable e -> e;
|
||||
case Get(var location, var left, var name) -> new Get(location, optimize(left), optimize(name));
|
||||
case At(var location, var left, var index) -> new At(location, optimize(left), optimize(index));
|
||||
case GetOrAt(var location, var left, var nameOrIndex) -> new GetOrAt(location, optimize(left), optimize(nameOrIndex));
|
||||
case Bind(var location, var callable, var parameter) -> new Bind(location, optimize(callable), optimize(parameter));
|
||||
case Call(var location, var callable, var arguments) -> new Call(location, optimize(callable), optimize(arguments));
|
||||
case Closure(var location, var boundArgs, var variadic, var steps, var finish) -> new Closure(location, boundArgs, variadic, optimize(steps, finish), optimize(finish));
|
||||
case ExprGroup(var location, var steps, var finish, ExprGroup.PackedArgs(var pFrom, var pTo, var variadic), var fork) -> new ExprGroup(location, optimize(steps, finish), new ExprGroup.PackedArgs(optimize(pFrom), pTo, variadic), fork);
|
||||
case ExprGroup(var location, var steps, var finish, var packedArgs, var fork) -> asDynamic(ExprGroup.of(location, optimize(steps, finish), fork));
|
||||
case ListLiteral(var location, var elements) -> new ListLiteral(location, elements.stream().map(Optimizer::optimize).toList());
|
||||
case ObjectLiteral e -> e; // Exclusively handled in optimize even though it contains expressions, since it cannot be decomposed
|
||||
};
|
||||
}
|
||||
|
||||
private static <T> List<T> concat(List<T> list, T element) {
|
||||
return Stream.concat(list.stream(), Stream.of(element)).toList();
|
||||
}
|
||||
|
||||
private static List<Expr> optimize(List<Expr> steps, DynamicExpr finish) {
|
||||
return Stream.concat(steps.stream().map(Optimizer::optimize).flatMap(Optimizer::extractSideEffects).map(Optimizer::optimize), Stream.of(optimize(finish))).toList();
|
||||
}
|
||||
|
||||
private static List<Call.Argument> optimize(List<Call.Argument> arguments) {
|
||||
return arguments.stream().map(arg -> new Call.Argument(optimize(arg.value()), arg.variadic())).toList();
|
||||
}
|
||||
|
||||
public static Stream<Expr> extractSideEffects(Expr expr) {
|
||||
return switch (expr) {
|
||||
case NullLiteral e -> Stream.empty();
|
||||
|
Loading…
Reference in New Issue
Block a user