[muscript] implement basic AST optimization. Completely untested, of course

This commit is contained in:
Johannes Frohnmeyer 2022-06-29 17:38:05 +02:00
parent 62ab9e930d
commit f84bdd2a02
Signed by: Johannes
GPG Key ID: E76429612C2929F4
38 changed files with 526 additions and 29 deletions

View File

@ -20,7 +20,7 @@ public class Parser {
private final TokenData current = new TokenData();
public static Expr<?> parse(String source) {
return new Parser(new Lexer(source)).parse();
return new Parser(new Lexer(source)).parse().optimize();
}
public Parser(Lexer lexer) {
@ -33,7 +33,6 @@ public class Parser {
}
// Expressions
private Expr<?> expression() {
try {
return conditional();

View File

@ -13,6 +13,9 @@ public abstract non-sealed class BoolExpr extends Expr<Boolean> {
return Type.Boolean;
}
@Override
public abstract BoolExpr optimize();
@Override
public DynamicExpr asDynamicExpr() {
return new DynamicCoerce(character, this);

View File

@ -14,6 +14,9 @@ public abstract non-sealed class DynamicExpr extends Expr<Dynamic<?>> {
return Type.Dynamic;
}
@Override
public abstract DynamicExpr optimize();
@Override
public BoolExpr asBoolExpr() {
return new BoolUnpack(character, this);

View File

@ -15,6 +15,7 @@ public sealed abstract class Expr<T> permits BoolExpr, DynamicExpr, NullLiteral,
public abstract Type getResultType();
public abstract T get(Dynamic<?> dataRoot);
public abstract Expr<T> optimize();
public final int character;
public BoolExpr asBoolExpr() {

View File

@ -23,6 +23,11 @@ public final class NullLiteral extends Expr<Object> {
return null;
}
@Override
public NullLiteral optimize() {
return this;
}
@Override
public DynamicExpr asDynamicExpr() {
return new DynamicLiteral<>(character, new DNull());
@ -42,4 +47,9 @@ public final class NullLiteral extends Expr<Object> {
public BoolExpr asBoolExpr() {
throw new LocationalException(character, "Attempted to convert null to a boolean");
}
@Override
public boolean equals(Object obj) {
return obj instanceof NullLiteral;
}
}

View File

@ -13,6 +13,9 @@ public abstract non-sealed class NumberExpr extends Expr<Double> {
return Type.Number;
}
@Override
public abstract NumberExpr optimize();
@Override
public DynamicExpr asDynamicExpr() {
return new DynamicCoerce(character, this);

View File

@ -13,6 +13,9 @@ public abstract non-sealed class StringExpr extends Expr<String> {
return Type.String;
}
@Override
public abstract StringExpr optimize();
@Override
public DynamicExpr asDynamicExpr() {
return new DynamicCoerce(character, this);

View File

@ -2,6 +2,8 @@ package io.gitlab.jfronny.muscript.compiler.expr.bool;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.BoolLiteral;
public class And extends BoolExpr {
private final BoolExpr left;
@ -17,4 +19,18 @@ public class And extends BoolExpr {
public Boolean get(Dynamic<?> dataRoot) {
return left.get(dataRoot) && right.get(dataRoot);
}
@Override
public BoolExpr optimize() {
BoolExpr left = this.left.optimize();
BoolExpr right = this.right.optimize();
if (left instanceof BoolLiteral literal) return literal.value ? right : Expr.literal(character, false);
if (right instanceof BoolLiteral literal) return literal.value ? left : Expr.literal(character, false);
return new And(character, left, right);
}
@Override
public boolean equals(Object obj) {
return obj instanceof And and && left.equals(and.left) && right.equals(and.right);
}
}

View File

@ -2,6 +2,8 @@ package io.gitlab.jfronny.muscript.compiler.expr.bool;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.BoolLiteral;
public class Not extends BoolExpr {
private final BoolExpr inner;
@ -15,4 +17,17 @@ public class Not extends BoolExpr {
public Boolean get(Dynamic<?> dataRoot) {
return !inner.get(dataRoot);
}
@Override
public BoolExpr optimize() {
BoolExpr inner = this.inner.optimize();
if (inner instanceof Not not) return not.inner;
if (inner instanceof BoolLiteral literal) return Expr.literal(character, !literal.value);
return new Not(character, inner);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Not not && inner.equals(not.inner);
}
}

View File

@ -2,6 +2,8 @@ package io.gitlab.jfronny.muscript.compiler.expr.bool;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.BoolLiteral;
public class Or extends BoolExpr {
private final BoolExpr left;
@ -17,4 +19,18 @@ public class Or extends BoolExpr {
public Boolean get(Dynamic<?> dataRoot) {
return left.get(dataRoot) || right.get(dataRoot);
}
@Override
public BoolExpr optimize() {
BoolExpr left = this.left.optimize();
BoolExpr right = this.right.optimize();
if (left instanceof BoolLiteral literal) return literal.value ? Expr.literal(character, true) : right;
if (right instanceof BoolLiteral literal) return literal.value ? Expr.literal(character, true) : left;
return new Or(character, left, right);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Or or && left.equals(or.left) && right.equals(or.right);
}
}

View File

@ -25,4 +25,17 @@ public class Equal extends BoolExpr {
if (o instanceof Dynamic<?> a) return unwrap(a.getValue());
return o;
}
@Override
public BoolExpr optimize() {
Expr<?> left = this.left.optimize();
Expr<?> right = this.right.optimize();
if (left.equals(right)) return Expr.literal(character, true);
return new Equal(character, left, right);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Equal equal && left.equals(equal.left) && right.equals(equal.right);
}
}

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.muscript.compiler.expr.common.conditional;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.BoolLiteral;
public class BoolConditional extends BoolExpr {
public final BoolExpr condition;
@ -19,4 +20,22 @@ public class BoolConditional extends BoolExpr {
public Boolean get(Dynamic<?> dataRoot) {
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
}
@Override
public BoolExpr optimize() {
BoolExpr condition = this.condition.optimize();
BoolExpr trueExpr = this.trueExpr.optimize();
BoolExpr falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
if (trueExpr.equals(falseExpr)) return trueExpr;
return new BoolConditional(character, condition, trueExpr, falseExpr);
}
@Override
public boolean equals(Object obj) {
return obj instanceof BoolConditional conditional
&& condition.equals(conditional.condition)
&& trueExpr.equals(conditional.trueExpr)
&& falseExpr.equals(conditional.falseExpr);
}
}

View File

@ -3,6 +3,7 @@ package io.gitlab.jfronny.muscript.compiler.expr.common.conditional;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.DynamicExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.BoolLiteral;
import io.gitlab.jfronny.muscript.error.TypeMismatchException;
public class DynamicConditional extends DynamicExpr {
@ -10,7 +11,7 @@ public class DynamicConditional extends DynamicExpr {
public final DynamicExpr trueExpr;
public final DynamicExpr falseExpr;
public DynamicConditional(int character, BoolExpr condition, DynamicExpr trueExpr, DynamicExpr falseExpr) throws TypeMismatchException {
public DynamicConditional(int character, BoolExpr condition, DynamicExpr trueExpr, DynamicExpr falseExpr) {
super(character);
this.condition = condition;
this.trueExpr = trueExpr;
@ -18,7 +19,25 @@ public class DynamicConditional extends DynamicExpr {
}
@Override
public Dynamic get(Dynamic<?> dataRoot) {
public Dynamic<?> get(Dynamic<?> dataRoot) {
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
}
@Override
public DynamicExpr optimize() {
BoolExpr condition = this.condition.optimize();
DynamicExpr trueExpr = this.trueExpr.optimize();
DynamicExpr falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
if (trueExpr.equals(falseExpr)) return trueExpr;
return new DynamicConditional(character, condition, trueExpr, falseExpr);
}
@Override
public boolean equals(Object obj) {
return obj instanceof DynamicConditional conditional
&& condition.equals(conditional.condition)
&& trueExpr.equals(conditional.trueExpr)
&& falseExpr.equals(conditional.falseExpr);
}
}

View File

@ -1,8 +1,8 @@
package io.gitlab.jfronny.muscript.compiler.expr.common.conditional;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.BoolLiteral;
public class NumberConditional extends NumberExpr {
public final BoolExpr condition;
@ -20,4 +20,22 @@ public class NumberConditional extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
}
@Override
public NumberExpr optimize() {
BoolExpr condition = this.condition.optimize();
NumberExpr trueExpr = this.trueExpr.optimize();
NumberExpr falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
if (trueExpr.equals(falseExpr)) return trueExpr;
return new NumberConditional(character, condition, trueExpr, falseExpr);
}
@Override
public boolean equals(Object obj) {
return obj instanceof NumberConditional conditional
&& condition.equals(conditional.condition)
&& trueExpr.equals(conditional.trueExpr)
&& falseExpr.equals(conditional.falseExpr);
}
}

View File

@ -1,8 +1,8 @@
package io.gitlab.jfronny.muscript.compiler.expr.common.conditional;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.StringExpr;
import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.BoolLiteral;
public class StringConditional extends StringExpr {
public final BoolExpr condition;
@ -20,4 +20,22 @@ public class StringConditional extends StringExpr {
public String get(Dynamic<?> dataRoot) {
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
}
@Override
public StringExpr optimize() {
BoolExpr condition = this.condition.optimize();
StringExpr trueExpr = this.trueExpr.optimize();
StringExpr falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr : falseExpr;
if (trueExpr.equals(falseExpr)) return trueExpr;
return new StringConditional(character, condition, trueExpr, falseExpr);
}
@Override
public boolean equals(Object obj) {
return obj instanceof StringConditional conditional
&& condition.equals(conditional.condition)
&& trueExpr.equals(conditional.trueExpr)
&& falseExpr.equals(conditional.falseExpr);
}
}

View File

@ -4,7 +4,7 @@ import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
public final class BoolLiteral extends BoolExpr {
private final boolean value;
public final boolean value;
public BoolLiteral(int character, boolean value) {
super(character);
@ -15,4 +15,14 @@ public final class BoolLiteral extends BoolExpr {
public Boolean get(Dynamic<?> dataRoot) {
return value;
}
@Override
public BoolExpr optimize() {
return this;
}
@Override
public boolean equals(Object obj) {
return obj instanceof BoolLiteral literal && value == literal.value;
}
}

View File

@ -15,4 +15,14 @@ public final class DynamicLiteral<T> extends DynamicExpr {
public Dynamic<T> get(Dynamic<?> dataRoot) {
return value;
}
@Override
public DynamicExpr optimize() {
return this;
}
@Override
public boolean equals(Object obj) {
return obj instanceof DynamicLiteral<?> literal && value.equals(literal.value);
}
}

View File

@ -4,7 +4,7 @@ import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
public final class NumberLiteral extends NumberExpr {
private final double value;
public final double value;
public NumberLiteral(int character, double value) {
super(character);
@ -15,4 +15,14 @@ public final class NumberLiteral extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return value;
}
@Override
public NumberExpr optimize() {
return this;
}
@Override
public boolean equals(Object obj) {
return obj instanceof NumberLiteral literal && value == literal.value;
}
}

View File

@ -4,7 +4,7 @@ import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.StringExpr;
public final class StringLiteral extends StringExpr {
private final String value;
public final String value;
public StringLiteral(int character, String value) {
super(character);
@ -15,4 +15,14 @@ public final class StringLiteral extends StringExpr {
public String get(Dynamic<?> dataRoot) {
return value;
}
@Override
public StringExpr optimize() {
return this;
}
@Override
public boolean equals(Object obj) {
return obj instanceof StringLiteral literal && value.equals(literal.value);
}
}

View File

@ -5,6 +5,7 @@ import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
import java.util.*;
import java.util.stream.Collectors;
@CanThrow @UncheckedDynamic
public class Call extends DynamicExpr {
@ -25,4 +26,17 @@ public class Call extends DynamicExpr {
throw e.locational(character);
}
}
@Override
public DynamicExpr optimize() {
DynamicExpr left = this.left.optimize();
List<DynamicExpr> args = new ArrayList<>();
for (DynamicExpr arg : this.args) args.add(arg.optimize());
return new Call(character, left, args);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Call call && left.equals(call.left) && args.equals(call.args);
}
}

View File

@ -2,9 +2,10 @@ package io.gitlab.jfronny.muscript.compiler.expr.dynamic;
import io.gitlab.jfronny.commons.data.dynamic.*;
import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.dynamic.unpack.*;
public class DynamicCoerce extends DynamicExpr {
private final Expr<?> inner;
public final Expr<?> inner;
public DynamicCoerce(int character, Expr<?> inner) {
super(character);
@ -27,4 +28,18 @@ public class DynamicCoerce extends DynamicExpr {
if (inner instanceof NullLiteral) return new DNull();
throw new IllegalStateException("The inner expression of DynamicCoerce should never be of undefined type");
}
@Override
public DynamicExpr optimize() {
Expr<?> inner = this.inner.optimize();
if (inner instanceof BoolUnpack unpack) return unpack.inner;
if (inner instanceof NumberUnpack unpack) return unpack.inner;
if (inner instanceof StringUnpack unpack) return unpack.inner;
return new DynamicCoerce(character, inner);
}
@Override
public boolean equals(Object obj) {
return obj instanceof DynamicCoerce coerce && inner.equals(coerce.inner);
}
}

View File

@ -32,4 +32,16 @@ public class Get extends DynamicExpr {
}
throw new DynamicTypeConversionException("object or list").locational(character);
}
@Override
public DynamicExpr optimize() {
DynamicExpr left = this.left.optimize();
Expr<?> name = this.name.optimize();
return new Get(character, left, name);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Get get && left.equals(get.left) && name.equals(get.name);
}
}

View File

@ -29,4 +29,14 @@ public class Variable extends DynamicExpr {
return res;
} else throw new LocationalException(character, "This object doesn't contain that name");
}
@Override
public DynamicExpr optimize() {
return this;
}
@Override
public boolean equals(Object obj) {
return obj instanceof Variable variable && name.equals(variable.name);
}
}

View File

@ -3,11 +3,12 @@ package io.gitlab.jfronny.muscript.compiler.expr.dynamic.unpack;
import io.gitlab.jfronny.commons.data.dynamic.*;
import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
import io.gitlab.jfronny.muscript.compiler.expr.dynamic.DynamicCoerce;
@CanThrow
@UncheckedDynamic
public class BoolUnpack extends BoolExpr {
private final DynamicExpr inner;
public final DynamicExpr inner;
public BoolUnpack(int character, DynamicExpr inner) {
super(character);
@ -22,4 +23,16 @@ public class BoolUnpack extends BoolExpr {
throw e.locational(character);
}
}
@Override
public BoolExpr optimize() {
DynamicExpr inner = this.inner.optimize();
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asBoolExpr();
return new BoolUnpack(character, inner);
}
@Override
public boolean equals(Object obj) {
return obj instanceof BoolUnpack unpack && inner.equals(unpack.inner);
}
}

View File

@ -5,11 +5,12 @@ import io.gitlab.jfronny.commons.data.dynamic.DynamicTypeConversionException;
import io.gitlab.jfronny.muscript.compiler.expr.DynamicExpr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.annotations.*;
import io.gitlab.jfronny.muscript.compiler.expr.dynamic.DynamicCoerce;
@CanThrow
@UncheckedDynamic
public class NumberUnpack extends NumberExpr {
private final DynamicExpr inner;
public final DynamicExpr inner;
public NumberUnpack(int character, DynamicExpr inner) {
super(character);
@ -24,4 +25,16 @@ public class NumberUnpack extends NumberExpr {
throw e.locational(character);
}
}
@Override
public NumberExpr optimize() {
DynamicExpr inner = this.inner.optimize();
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asNumberExpr();
return new NumberUnpack(character, inner.optimize());
}
@Override
public boolean equals(Object obj) {
return obj instanceof NumberUnpack unpack && inner.equals(unpack.inner);
}
}

View File

@ -6,11 +6,12 @@ import io.gitlab.jfronny.muscript.compiler.expr.DynamicExpr;
import io.gitlab.jfronny.muscript.compiler.expr.StringExpr;
import io.gitlab.jfronny.muscript.compiler.expr.annotations.CanThrow;
import io.gitlab.jfronny.muscript.compiler.expr.annotations.UncheckedDynamic;
import io.gitlab.jfronny.muscript.compiler.expr.dynamic.DynamicCoerce;
@CanThrow
@UncheckedDynamic
public class StringUnpack extends StringExpr {
private final DynamicExpr inner;
public final DynamicExpr inner;
public StringUnpack(int character, DynamicExpr inner) {
super(character);
@ -25,4 +26,16 @@ public class StringUnpack extends StringExpr {
throw e.locational(character);
}
}
@Override
public StringExpr optimize() {
DynamicExpr inner = this.inner.optimize();
if (inner instanceof DynamicCoerce coerce) return coerce.inner.asStringExpr();
return new StringUnpack(character, inner);
}
@Override
public boolean equals(Object obj) {
return obj instanceof StringUnpack unpack && inner.equals(unpack.inner);
}
}

View File

@ -1,8 +1,10 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.compare;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.bool.Not;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
import io.gitlab.jfronny.muscript.compiler.expr.number.math.*;
public class Greater extends BoolExpr {
private final NumberExpr left;
@ -18,4 +20,24 @@ public class Greater extends BoolExpr {
public Boolean get(Dynamic<?> dataRoot) {
return left.get(dataRoot) > right.get(dataRoot);
}
@Override
public BoolExpr optimize() {
NumberExpr left = this.left.optimize();
NumberExpr right = this.right.optimize();
if (left instanceof NumberLiteral litL && right instanceof NumberLiteral litR) return Expr.literal(character, litL.value > litR.value);
if (left instanceof Divide divide) return new Greater(character, divide.dividend, new Multiply(divide.character, right, divide.divisor)).optimize();
if (left instanceof Invert invert) return new Less(character, invert.inner, new Invert(right.character, right)).optimize();
if (left instanceof Minus minus) return new Greater(character, minus.minuend, new Plus(minus.character, minus.subtrahend, right)).optimize();
// 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 Plus plus) return new Greater(character, plus.augend, new Minus(plus.character, plus.addend, right)).optimize();
// Power is left out because it can't be transformed cleanly either
return new Greater(character, left, right);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Greater greater && left.equals(greater.left) && right.equals(greater.right);
}
}

View File

@ -1,8 +1,9 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.compare;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.BoolExpr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
import io.gitlab.jfronny.muscript.compiler.expr.number.math.*;
public class Less extends BoolExpr {
private final NumberExpr left;
@ -18,4 +19,24 @@ public class Less extends BoolExpr {
public Boolean get(Dynamic<?> dataRoot) {
return left.get(dataRoot) < right.get(dataRoot);
}
@Override
public BoolExpr optimize() {
NumberExpr left = this.left.optimize();
NumberExpr right = this.right.optimize();
if (left instanceof NumberLiteral litL && right instanceof NumberLiteral litR) return Expr.literal(character, litL.value < litR.value);
if (left instanceof Divide divide) return new Less(character, divide.dividend, new Multiply(divide.character, divide.divisor, right)).optimize();
if (left instanceof Invert invert) return new Greater(character, invert.inner, new Invert(right.character, right)).optimize();
if (left instanceof Minus minus) return new Less(character, minus.minuend, new Plus(minus.character, minus.subtrahend, right)).optimize();
// 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 Plus plus) return new Less(character, plus.augend, new Minus(plus.character, plus.addend, right)).optimize();
// Power is left out because it can't be transformed cleanly either
return new Less(character, left, right);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Less less && left.equals(less.left) && right.equals(less.right);
}
}

View File

@ -1,11 +1,13 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.math;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
public class Divide extends NumberExpr {
private final NumberExpr dividend;
private final NumberExpr divisor;
public final NumberExpr dividend;
public final NumberExpr divisor;
public Divide(int character, NumberExpr dividend, NumberExpr divisor) {
super(character);
@ -17,4 +19,18 @@ public class Divide extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return dividend.get(dataRoot) / divisor.get(dataRoot);
}
@Override
public NumberExpr optimize() {
NumberExpr dividend = this.dividend.optimize();
NumberExpr divisor = this.divisor.optimize();
if (dividend instanceof NumberLiteral litL && divisor instanceof NumberLiteral litR) return Expr.literal(character, litL.value / litR.value);
if (dividend instanceof Divide divide && divide.dividend instanceof NumberLiteral literal) return new Divide(character, divide.dividend, new Multiply(divide.character, divisor, literal)).optimize();
return new Divide(character, dividend, divisor);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Divide divide && dividend.equals(divide.dividend) && divisor.equals(divide.divisor);
}
}

View File

@ -1,10 +1,12 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.math;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
public class Invert extends NumberExpr {
private final NumberExpr inner;
public final NumberExpr inner;
public Invert(int character, NumberExpr inner) {
super(character);
@ -15,4 +17,17 @@ public class Invert extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return -inner.get(dataRoot);
}
@Override
public NumberExpr optimize() {
NumberExpr inner = this.inner.optimize();
if (inner instanceof Invert invert) return invert.inner;
if (inner instanceof NumberLiteral literal) return Expr.literal(character, -literal.value);
return new Invert(character, inner);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Invert invert && inner.equals(invert.inner);
}
}

View File

@ -1,11 +1,13 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.math;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
public class Minus extends NumberExpr {
private final NumberExpr minuend;
private final NumberExpr subtrahend;
public final NumberExpr minuend;
public final NumberExpr subtrahend;
public Minus(int character, NumberExpr minuend, NumberExpr subtrahend) {
super(character);
@ -17,4 +19,18 @@ public class Minus extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return minuend.get(dataRoot) - subtrahend.get(dataRoot);
}
@Override
public NumberExpr optimize() {
NumberExpr minuend = this.minuend.optimize();
NumberExpr subtrahend = this.subtrahend.optimize();
if (minuend instanceof NumberLiteral litM && subtrahend instanceof NumberLiteral litS) return Expr.literal(character, litM.value - litS.value);
if (minuend instanceof Minus minus) return new Minus(character, minus.minuend, new Plus(minus.character, minus.subtrahend, subtrahend)).optimize();
return new Minus(character, minuend, subtrahend);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Minus minus && minuend.equals(minus.minuend) && subtrahend.equals(minus.subtrahend);
}
}

View File

@ -1,7 +1,9 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.math;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
public class Modulo extends NumberExpr {
private final NumberExpr dividend;
@ -17,4 +19,17 @@ public class Modulo extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return dividend.get(dataRoot) % divisor.get(dataRoot);
}
@Override
public NumberExpr optimize() {
NumberExpr dividend = this.dividend.optimize();
NumberExpr divisor = this.divisor.optimize();
if (dividend instanceof NumberLiteral litD && divisor instanceof NumberLiteral litS) return Expr.literal(character, litD.value % litS.value);
return new Modulo(character, dividend, divisor);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Modulo modulo && dividend.equals(modulo.dividend) && divisor.equals(modulo.divisor);
}
}

View File

@ -1,11 +1,13 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.math;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
public class Multiply extends NumberExpr {
private final NumberExpr multiplier;
private final NumberExpr multiplicand;
public final NumberExpr multiplier;
public final NumberExpr multiplicand;
public Multiply(int character, NumberExpr multiplier, NumberExpr multiplicand) {
super(character);
@ -17,4 +19,17 @@ public class Multiply extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return multiplier.get(dataRoot) * multiplicand.get(dataRoot);
}
@Override
public NumberExpr optimize() {
NumberExpr multiplier = this.multiplier.optimize();
NumberExpr multiplicand = this.multiplicand.optimize();
if (multiplier instanceof NumberLiteral litEr && multiplicand instanceof NumberLiteral litAnd) return Expr.literal(character, litEr.value * litAnd.value);
return new Modulo(character, multiplier, multiplicand);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Multiply multiply && multiplier.equals(multiply.multiplier) && multiplicand.equals(multiply.multiplicand);
}
}

View File

@ -1,11 +1,13 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.math;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
public class Plus extends NumberExpr {
private final NumberExpr augend;
private final NumberExpr addend;
public final NumberExpr augend;
public final NumberExpr addend;
public Plus(int character, NumberExpr augend, NumberExpr addend) {
super(character);
@ -17,4 +19,17 @@ public class Plus extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return augend.get(dataRoot) + addend.get(dataRoot);
}
@Override
public NumberExpr optimize() {
NumberExpr augend = this.augend.optimize();
NumberExpr addend = this.addend.optimize();
if (augend instanceof NumberLiteral litU && addend instanceof NumberLiteral litD) return Expr.literal(character, litU.value + litD.value);
return new Plus(character, augend, addend);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Plus plus && augend.equals(plus.augend) && addend.equals(plus.addend);
}
}

View File

@ -1,7 +1,9 @@
package io.gitlab.jfronny.muscript.compiler.expr.number.math;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.Expr;
import io.gitlab.jfronny.muscript.compiler.expr.NumberExpr;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.NumberLiteral;
public class Power extends NumberExpr {
private final NumberExpr base;
@ -17,4 +19,27 @@ public class Power extends NumberExpr {
public Double get(Dynamic<?> dataRoot) {
return Math.pow(base.get(dataRoot), exponent.get(dataRoot));
}
@Override
public NumberExpr optimize() {
NumberExpr base = this.base.optimize();
NumberExpr exponent = this.exponent.optimize();
if (base instanceof NumberLiteral litB && exponent instanceof NumberLiteral litE) return Expr.literal(character, Math.pow(litB.value, litE.value));
if (exponent instanceof NumberLiteral exp && base instanceof Multiply multiply) {
if (multiply.multiplier instanceof NumberLiteral literal)
return new Multiply(multiply.character,
Expr.literal(character, Math.pow(literal.value, exp.value)),
new Power(character, multiply.multiplicand, exp));
if (multiply.multiplicand instanceof NumberLiteral literal)
return new Multiply(multiply.character,
Expr.literal(character, Math.pow(literal.value, exp.value)),
new Power(character, multiply.multiplier, exp));
}
return new Power(character, base, exponent);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Power power && base.equals(power.base) && exponent.equals(power.exponent);
}
}

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.muscript.compiler.expr.string;
import io.gitlab.jfronny.commons.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.StringLiteral;
public class Concatenate extends StringExpr {
private final StringExpr left;
@ -17,4 +18,23 @@ public class Concatenate extends StringExpr {
public String get(Dynamic<?> dataRoot) {
return left.get(dataRoot) + right.get(dataRoot);
}
@Override
public StringExpr optimize() {
StringExpr left = this.left.optimize();
StringExpr right = this.right.optimize();
if (left instanceof StringLiteral litL && right instanceof StringLiteral litR) return Expr.literal(character, litL.value + litR.value);
if (right instanceof StringLiteral litR && left instanceof Concatenate concatenate && concatenate.right instanceof StringLiteral litL) {
return new Concatenate(character, concatenate.left, Expr.literal(concatenate.character, litL.value + litR.value));
}
if (left instanceof StringLiteral litL && right instanceof Concatenate concatenate && concatenate.left instanceof StringLiteral litR) {
return new Concatenate(character, Expr.literal(concatenate.character, litL.value + litR.value), concatenate.right);
}
return new Concatenate(character, left, right);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Concatenate concatenate && left.equals(concatenate.left) && right.equals(concatenate.right);
}
}

View File

@ -17,4 +17,16 @@ public class StringCoerce extends StringExpr {
public String get(Dynamic<?> dataRoot) {
return StringFormatter.toString(inner.get(dataRoot));
}
@Override
public StringExpr optimize() {
Expr<?> inner = this.inner.optimize();
if (inner instanceof StringExpr expr) return expr;
return new StringCoerce(character, inner);
}
@Override
public boolean equals(Object obj) {
return obj instanceof StringCoerce coerce && inner.equals(coerce.inner);
}
}

View File

@ -5,6 +5,7 @@ import io.gitlab.jfronny.muscript.compiler.Type;
import io.gitlab.jfronny.muscript.compiler.expr.*;
import io.gitlab.jfronny.muscript.compiler.expr.annotations.CanThrow;
import io.gitlab.jfronny.muscript.compiler.expr.common.conditional.*;
import io.gitlab.jfronny.muscript.compiler.expr.common.literal.BoolLiteral;
@CanThrow
public class UnresolvedConditional extends DynamicExpr {
@ -25,7 +26,17 @@ public class UnresolvedConditional extends DynamicExpr {
}
@Override
public Dynamic get(Dynamic dataRoot) {
public DynamicExpr optimize() {
BoolExpr condition = this.condition.optimize();
Expr<?> trueExpr = this.trueExpr.optimize();
Expr<?> falseExpr = this.falseExpr.optimize();
if (condition instanceof BoolLiteral literal) return literal.value ? trueExpr.asDynamicExpr() : falseExpr.asDynamicExpr();
if (trueExpr.equals(falseExpr)) return trueExpr.asDynamicExpr();
return new UnresolvedConditional(character, condition, trueExpr, falseExpr);
}
@Override
public Dynamic<?> get(Dynamic<?> dataRoot) {
throw new UnsupportedOperationException("Conditional was kept unresolved. This is not supported!");
}
@ -48,4 +59,12 @@ public class UnresolvedConditional extends DynamicExpr {
public NumberExpr asNumberExpr() {
return new NumberConditional(character, condition, trueExpr.asNumberExpr(), falseExpr.asNumberExpr());
}
@Override
public boolean equals(Object obj) {
return obj instanceof UnresolvedConditional conditional
&& condition.equals(conditional.condition)
&& trueExpr.equals(conditional.trueExpr)
&& falseExpr.equals(conditional.falseExpr);
}
}