Support for assignment operators and closures
ci/woodpecker/push/woodpecker Pipeline failed Details

This commit is contained in:
Johannes Frohnmeyer 2023-01-20 21:05:04 +01:00
parent c8f6b0b6c4
commit 2847e05440
Signed by: Johannes
GPG Key ID: E76429612C2929F4
64 changed files with 867 additions and 108 deletions

View File

@ -71,7 +71,7 @@ one
by calling `as(Bool|String|Number|Dynamic)Expr`.
This process may throw a ParseException.
You can call `get(Dynamic<?> dataRoot)` on the result to execute the script on the provided data, which should be an
`ExpressionParameter` on which you called `StandardLib.addTo()` to add standard methods.
`Scope` on which you called `StandardLib.addTo()` to add standard methods.
This is also where you can add custom data to be accessed by your script.
The execution of a script can throw a LocationalException which may be converted to a LocationalError for printing
using the source of the expression if available.
@ -95,7 +95,7 @@ try {
System.err.println(e.asPrintable(source));
return;
}
ExpressionParameter parameter = StandardLib.addTo(new ExpressionParameter())
Scope scope = StandardLib.addTo(new Scope())
.set("someValue", DFinal.of(15))
.set("someOther", DFinal.of(Map.of(
"subValue", DFinal.of(true)

View File

@ -1,8 +1,8 @@
package io.gitlab.jfronny.muscript;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DList;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.ExpressionParameter;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -16,8 +16,8 @@ public class StandardLib {
public static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
public static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd. MM. yyyy");
public static ExpressionParameter addTo(ExpressionParameter parameter) {
return parameter
public static Scope addTo(Scope scope) {
return scope
.set("PI", of(Math.PI))
.set("time", of(timeFormat.format(new Date())))
.set("date", of(dateFormat.format(new Date())))

View File

@ -1,21 +1,30 @@
package io.gitlab.jfronny.muscript.ast;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.annotations.CanThrow;
import io.gitlab.jfronny.muscript.ast.literal.*;
import io.gitlab.jfronny.muscript.ast.string.StringCoerce;
import io.gitlab.jfronny.muscript.compiler.Type;
import io.gitlab.jfronny.muscript.data.dynamic.DObject;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.error.TypeMismatchException;
@CanThrow
public sealed abstract class Expr<T> permits BoolExpr, DynamicExpr, NullLiteral, NumberExpr, StringExpr {
public abstract sealed class Expr<T> permits BoolExpr, DynamicExpr, NullLiteral, NumberExpr, StringExpr {
protected Expr(int chStart, int chEnd) {
this.chStart = chStart;
this.chEnd = chEnd;
}
public abstract Type getResultType();
public abstract T get(Dynamic<?> dataRoot);
public abstract T get(Scope dataRoot);
public T get(DObject dataRoot) {
return get(dataRoot instanceof Scope scope ? scope : new Scope(dataRoot));
}
@Deprecated
public T get(Dynamic<?> dataRoot) {
return get(dataRoot.asObject());
}
public abstract Expr<T> optimize();
public final int chStart;
public final int chEnd;

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.muscript.ast;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DNull;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.annotations.CanThrow;
import io.gitlab.jfronny.muscript.ast.literal.DynamicLiteral;
import io.gitlab.jfronny.muscript.compiler.Type;
@ -19,7 +19,7 @@ public final class NullLiteral extends Expr<Object> {
}
@Override
public Object get(Dynamic<?> dataRoot) {
public Object get(Scope dataRoot) {
return null;
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.bool;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
@ -16,7 +16,7 @@ public class And extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
return left.get(dataRoot) && right.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.bool;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
@ -14,7 +14,7 @@ public class Not extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
return !inner.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.bool;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
@ -16,7 +16,7 @@ public class Or extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
return left.get(dataRoot) || right.get(dataRoot);
}

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.ast.compare;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.Expr;
@ -17,7 +18,7 @@ public class Equal extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
return Objects.equals(unwrap(left.get(dataRoot)), unwrap(right.get(dataRoot)));
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.compare;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.*;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
import io.gitlab.jfronny.muscript.ast.math.*;
@ -16,7 +16,7 @@ public class Greater extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
return left.get(dataRoot) > right.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.compare;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.*;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
import io.gitlab.jfronny.muscript.ast.math.*;
@ -16,7 +16,7 @@ public class Less extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
return left.get(dataRoot) < right.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
@ -17,7 +17,7 @@ public class BoolConditional extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
}

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
@ -18,7 +19,7 @@ public class DynamicConditional extends DynamicExpr {
}
@Override
public Dynamic<?> get(Dynamic<?> dataRoot) {
public Dynamic<?> get(Scope dataRoot) {
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
@ -18,7 +18,7 @@ public class NumberConditional extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.StringExpr;
import io.gitlab.jfronny.muscript.ast.literal.BoolLiteral;
@ -18,7 +18,7 @@ public class StringConditional extends StringExpr {
}
@Override
public String get(Dynamic<?> dataRoot) {
public String get(Scope dataRoot) {
return condition.get(dataRoot) ? trueExpr.get(dataRoot) : falseExpr.get(dataRoot);
}

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.ast.conditional;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.ast.*;
import io.gitlab.jfronny.muscript.annotations.CanThrow;
@ -35,7 +36,7 @@ public class UnresolvedConditional extends DynamicExpr {
}
@Override
public Dynamic<?> get(Dynamic<?> dataRoot) {
public Dynamic<?> get(Scope dataRoot) {
throw new UnsupportedOperationException("Conditional was kept unresolved. This is not supported!");
}

View File

@ -0,0 +1,35 @@
package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
public class Assign extends DynamicExpr {
private final String name;
private final DynamicExpr value;
public Assign(int chStart, int chEnd, String name, DynamicExpr value) {
super(chStart, chEnd);
this.name = name;
this.value = value;
}
@Override
public Dynamic<?> get(Scope dataRoot) {
Dynamic<?> data = value.get(dataRoot);
dataRoot.set(name, data);
return data;
}
@Override
public DynamicExpr optimize() {
return new Assign(chStart, chEnd, name, value.optimize());
}
@Override
public boolean equals(Object obj) {
return obj instanceof Assign assign
&& name.equals(assign.name)
&& value.equals(assign.value);
}
}

View File

@ -3,6 +3,7 @@ package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.annotations.CanThrow;
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import java.util.ArrayList;
@ -21,7 +22,7 @@ public class Call extends DynamicExpr {
}
@Override
public Dynamic<?> get(Dynamic<?> dataRoot) {
public Dynamic<?> get(Scope dataRoot) {
try {
return left.get(dataRoot).asCallable().getValue().apply(DFinal.of(args.stream().map(e -> e.get(dataRoot)).toArray(Dynamic[]::new)));
} catch (DynamicTypeConversionException e) {

View File

@ -0,0 +1,53 @@
package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import io.gitlab.jfronny.muscript.error.LocationalException;
import java.util.List;
public class Closure extends DynamicExpr {
private final List<String> boundArgs;
private final List<Expr<?>> steps;
private final DynamicExpr fin;
public Closure(int chStart, int chEnd, List<String> boundArgs, List<Expr<?>> expressions) {
this(chStart, chEnd, boundArgs, expressions.subList(0, expressions.size() - 1), expressions.get(expressions.size() - 1).asDynamicExpr());
}
private Closure(int chStart, int chEnd, List<String> boundArgs, List<Expr<?>> steps, DynamicExpr fin) {
super(chStart, chEnd);
this.boundArgs = List.copyOf(boundArgs);
this.steps = List.copyOf(steps);
this.fin = fin;
}
@Override
public DCallable get(Scope dataRoot) {
return DFinal.of(args -> {
if (args.size() != boundArgs.size()) throw new LocationalException(chStart, chEnd, "Invoked with unexpected number of parameters");
Scope fork = dataRoot.fork();
for (int i = 0; i < boundArgs.size(); i++) fork.set(boundArgs.get(i), args.get(i));
for (Expr<?> step : steps) {
step.get(fork);
}
return fin.get(fork);
});
}
@Override
public DynamicExpr optimize() {
// Eliminating side effect free steps might be possible, but would be too much work imo
return new Closure(chStart, chEnd, boundArgs, steps.stream().<Expr<?>>map(Expr::optimize).toList(), fin.optimize());
}
@Override
public boolean equals(Object obj) {
return obj instanceof Closure closure
&& boundArgs.equals(closure.boundArgs)
&& steps.equals(closure.steps)
&& fin.equals(closure.fin);
}
}

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.ast.*;
import io.gitlab.jfronny.muscript.ast.dynamic.unpack.*;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*;
public class DynamicCoerce extends DynamicExpr {
@ -20,7 +21,7 @@ public class DynamicCoerce extends DynamicExpr {
}
@Override
public Dynamic<?> get(Dynamic<?> dataRoot) {
public Dynamic<?> get(Scope dataRoot) {
if (inner instanceof DynamicExpr e) return e.get(dataRoot);
if (inner instanceof BoolExpr e) return DFinal.of(e.get(dataRoot));
if (inner instanceof StringExpr e) return DFinal.of(e.get(dataRoot));

View File

@ -5,6 +5,7 @@ import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.annotations.CanThrow;
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
import io.gitlab.jfronny.muscript.compiler.Type;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import io.gitlab.jfronny.muscript.error.TypeMismatchException;
@ -24,7 +25,7 @@ public class Get extends DynamicExpr {
}
@Override
public Dynamic<?> get(Dynamic<?> dataRoot) {
public Dynamic<?> get(Scope dataRoot) {
Dynamic<?> left = this.left.get(dataRoot);
if (left instanceof DObject o) {
return o.get(name.asStringExpr().get(dataRoot));

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.annotations.CanThrow;
@ -16,8 +17,12 @@ public class Variable extends DynamicExpr {
this.name = name;
}
public Assign assign(DynamicExpr value) {
return new Assign(chStart, chEnd, name, value);
}
@Override
public Dynamic<?> get(Dynamic<?> dataRoot) {
public Dynamic<?> get(Scope dataRoot) {
if (dataRoot.asObject().has(name)) return dataRoot.asObject().get(name);
else if (name.contains("::")) {
Dynamic<?> res = dataRoot;

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
@ -19,7 +19,7 @@ public class BoolUnpack extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
try {
return inner.get(dataRoot).asBool().getValue();
} catch (DynamicTypeConversionException e) {

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
@ -19,7 +19,7 @@ public class NumberUnpack extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
try {
return inner.get(dataRoot).asNumber().getValue();
} catch (DynamicTypeConversionException e) {

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.dynamic.unpack;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.DynamicTypeConversionException;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.ast.StringExpr;
@ -19,7 +19,7 @@ public class StringUnpack extends StringExpr {
}
@Override
public String get(Dynamic<?> dataRoot) {
public String get(Scope dataRoot) {
try {
return inner.get(dataRoot).asString().getValue();
} catch (DynamicTypeConversionException e) {

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.literal;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.BoolExpr;
public final class BoolLiteral extends BoolExpr {
@ -12,7 +12,7 @@ public final class BoolLiteral extends BoolExpr {
}
@Override
public Boolean get(Dynamic<?> dataRoot) {
public Boolean get(Scope dataRoot) {
return value;
}

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.ast.literal;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
@ -12,7 +13,7 @@ public final class DynamicLiteral<T> extends DynamicExpr {
}
@Override
public Dynamic<T> get(Dynamic<?> dataRoot) {
public Dynamic<T> get(Scope dataRoot) {
return value;
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.literal;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
public final class NumberLiteral extends NumberExpr {
@ -12,7 +12,7 @@ public final class NumberLiteral extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return value;
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.literal;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.StringExpr;
public final class StringLiteral extends StringExpr {
@ -12,7 +12,7 @@ public final class StringLiteral extends StringExpr {
}
@Override
public String get(Dynamic<?> dataRoot) {
public String get(Scope dataRoot) {
return value;
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -16,7 +16,7 @@ public class Divide extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return dividend.get(dataRoot) / divisor.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -14,7 +14,7 @@ public class Invert extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return -inner.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -16,7 +16,7 @@ public class Minus extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return minuend.get(dataRoot) - subtrahend.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -16,7 +16,7 @@ public class Modulo extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return dividend.get(dataRoot) % divisor.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -16,7 +16,7 @@ public class Multiply extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return multiplier.get(dataRoot) * multiplicand.get(dataRoot);
}
@ -26,7 +26,7 @@ public class Multiply extends NumberExpr {
NumberExpr multiplicand = this.multiplicand.optimize();
if (multiplier instanceof NumberLiteral litEr && multiplicand instanceof NumberLiteral litAnd)
return Expr.literal(chStart, chEnd, litEr.value * litAnd.value);
return new Modulo(chStart, chEnd, multiplier, multiplicand);
return new Multiply(chStart, chEnd, multiplier, multiplicand);
}
@Override

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -16,7 +16,7 @@ public class Plus extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return augend.get(dataRoot) + addend.get(dataRoot);
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.math;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.NumberExpr;
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
@ -16,7 +16,7 @@ public class Power extends NumberExpr {
}
@Override
public Double get(Dynamic<?> dataRoot) {
public Double get(Scope dataRoot) {
return Math.pow(base.get(dataRoot), exponent.get(dataRoot));
}

View File

@ -1,6 +1,6 @@
package io.gitlab.jfronny.muscript.ast.string;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.StringExpr;
import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
@ -16,7 +16,7 @@ public class Concatenate extends StringExpr {
}
@Override
public String get(Dynamic<?> dataRoot) {
public String get(Scope dataRoot) {
return left.get(dataRoot) + right.get(dataRoot);
}

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.muscript.ast.string;
import io.gitlab.jfronny.commons.StringFormatter;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.ast.*;
import io.gitlab.jfronny.muscript.ast.literal.*;
@ -14,7 +14,7 @@ public class StringCoerce extends StringExpr {
}
@Override
public String get(Dynamic<?> dataRoot) {
public String get(Scope dataRoot) {
return StringFormatter.toString(inner.get(dataRoot));
}

View File

@ -48,12 +48,15 @@ public class Lexer {
case '=' -> {
if (match('=')) createToken(Token.EqualEqual);
else unexpected();
else createToken(Token.Assign);
}
case '!' -> createToken(match('=') ? Token.BangEqual : Token.Bang);
case '+' -> createToken(Token.Plus);
case '-' -> createToken(Token.Minus);
case '-' -> {
if (match('>')) createToken(Token.Arrow);
else createToken(Token.Minus);
}
case '*' -> createToken(Token.Star);
case '/' -> createToken(Token.Slash);
case '%' -> createToken(Token.Percentage);
@ -72,6 +75,8 @@ public class Lexer {
case ')' -> createToken(Token.RightParen);
case '[' -> createToken(Token.LeftBracket);
case ']' -> createToken(Token.RightBracket);
case '{' -> createToken(Token.LeftBrace);
case '}' -> createToken(Token.RightBrace);
default -> unexpected();
}

View File

@ -7,10 +7,11 @@ import io.gitlab.jfronny.muscript.ast.conditional.UnresolvedConditional;
import io.gitlab.jfronny.muscript.ast.dynamic.*;
import io.gitlab.jfronny.muscript.ast.math.*;
import io.gitlab.jfronny.muscript.ast.string.Concatenate;
import io.gitlab.jfronny.muscript.data.Script;
import io.gitlab.jfronny.muscript.error.*;
import java.util.ArrayList;
import java.util.List;
import java.text.ParseException;
import java.util.*;
public class Parser {
private final Lexer lexer;
@ -22,6 +23,10 @@ public class Parser {
return new Parser(new Lexer(source)).parse().optimize();
}
public static Script parseScript(String source) {
return new Parser(new Lexer(source)).parseScript().optimize();
}
public Parser(Lexer lexer) {
this.lexer = lexer;
}
@ -35,15 +40,31 @@ public class Parser {
public Expr<?> parse() {
advance();
Expr<?> expr = expression();
if (!lexer.token.equals(Token.EOF))
if (!isAtEnd())
throw new ParseException(LocationalError.create(lexer.source, lexer.start, lexer.current - 1, "Unexpected element after end of expression"));
return expr;
}
/**
* Generate a script instance.
* Multiple instructions will be executed in sequence and the result of the last one will be returned.
*
* @return the resulting expression
*/
public Script parseScript() {
advance();
List<Expr<?>> expressions = new LinkedList<>();
while (!isAtEnd()) {
expressions.add(expression());
}
if (expressions.isEmpty()) throw new ParseException(LocationalError.create(lexer.source, lexer.start, lexer.current - 1, "Missing any elements in closure"));
return new Script(expressions);
}
// Expressions
private Expr<?> expression() {
try {
return conditional();
return assignment();
} catch (RuntimeException e) {
if (e instanceof ParseException) throw e;
else if (e instanceof LocationalException le) {
@ -52,6 +73,20 @@ public class Parser {
}
}
private Expr<?> assignment() {
Expr<?> expr = conditional();
if (match(Token.Assign)) {
if (expr instanceof Variable variable) {
expr = variable.assign(expression().asDynamicExpr());
} else {
throw error("Attempted to assign non-variable. This is not supported!", expr);
}
}
return expr;
}
private Expr<?> conditional() {
Expr<?> expr = and();
@ -258,6 +293,24 @@ public class Parser {
return expr;
}
if (match(Token.LeftBrace)) {
int start = previous.start;
List<String> boundArgs = new LinkedList<>();
boolean first = true;
while (!match(Token.Arrow)) {
if (!first) consume(Token.Comma, "Closure parameters MUST be comma-seperated");
first = false;
consume(Token.Identifier, "Closure arguments MUST be identifiers");
boundArgs.add(previous.lexeme);
}
List<Expr<?>> expressions = new LinkedList<>();
while (!match(Token.RightBrace)) {
expressions.add(expression());
}
int end = previous.start;
return new Closure(start, end, boundArgs, expressions);
}
throw error("Expected expression.");
}

View File

@ -7,6 +7,7 @@ public enum Token {
True, False,
And, Or,
Assign,
EqualEqual, BangEqual,
Concat,
@ -23,5 +24,8 @@ public enum Token {
LeftParen, RightParen,
LeftBracket, RightBracket,
LeftBrace, RightBrace,
Arrow,
Error, EOF
}

View File

@ -1,23 +0,0 @@
package io.gitlab.jfronny.muscript.data;
import io.gitlab.jfronny.muscript.data.dynamic.DObject;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import java.util.HashMap;
import java.util.Map;
public record ExpressionParameter(Map<String, Dynamic<?>> value) implements DObject {
public ExpressionParameter() {
this(new HashMap<>());
}
@Override
public Map<String, Dynamic<?>> getValue() {
return value;
}
public ExpressionParameter set(String key, Dynamic<?> value) {
this.value.put(key, value);
return this;
}
}

View File

@ -0,0 +1,36 @@
package io.gitlab.jfronny.muscript.data;
import io.gitlab.jfronny.commons.data.ImmCollection;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import java.util.HashMap;
import java.util.Map;
public class Scope implements DObject {
private final DObject source;
private final Map<String, Dynamic<?>> override = new HashMap<>();
public Scope() {
this(DFinal.of(Map.of()));
}
public Scope(DObject source) {
this.source = source;
}
@Override
public Map<String, Dynamic<?>> getValue() {
var map = new HashMap<>(source.getValue());
map.putAll(override);
return ImmCollection.of(map);
}
public Scope set(String key, Dynamic<?> value) {
override.put(key, value);
return this;
}
public Scope fork() {
return new Scope(this);
}
}

View File

@ -0,0 +1,43 @@
package io.gitlab.jfronny.muscript.data;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import java.util.List;
public class Script {
private final List<Expr<?>> steps;
private final DynamicExpr fin;
public Script(List<Expr<?>> expressions) {
this(expressions.subList(0, expressions.size() - 1), expressions.get(expressions.size() - 1).asDynamicExpr());
}
private Script(List<Expr<?>> steps, DynamicExpr fin) {
this.steps = steps;
this.fin = fin;
}
public Dynamic<?> run(DObject scope) {
return run(new Scope(scope));
}
public Dynamic<?> run(Scope scope) {
for (Expr<?> expression : steps) {
expression.get(scope);
}
return fin.get(scope);
}
public DCallable bindTo(Scope scope) {
return DFinal.of(args -> {
scope.set("args", args);
return run(scope);
});
}
public Script optimize() {
return new Script(steps.stream().<Expr<?>>map(Expr::optimize).toList(), fin.optimize());
}
}

View File

@ -0,0 +1,22 @@
package io.gitlab.jfronny.muscript.test;
import io.gitlab.jfronny.muscript.ast.StringExpr;
import io.gitlab.jfronny.muscript.ast.dynamic.Assign;
import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
import io.gitlab.jfronny.muscript.compiler.Parser;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.test.util.UnforkableScope;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class AssignTest {
@Test
void testAssignSimple() {
StringExpr expr = Parser.parse("someval = 'test'").asStringExpr();
assertEquals(new Assign(0, 6, "someval", new StringLiteral(10, 15, "test").asDynamicExpr()).asStringExpr(), expr);
Scope scope = new UnforkableScope();
assertEquals("test", expr.get(scope));
assertEquals("test", scope.getValue().get("someval").asString().getValue());
}
}

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.muscript.test;
import org.junit.jupiter.api.*;
import static io.gitlab.jfronny.muscript.test.MuTestUtil.*;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class BooleanTest {

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.muscript.test;
import org.junit.jupiter.api.*;
import static io.gitlab.jfronny.muscript.test.MuTestUtil.*;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class CallableTest {

View File

@ -0,0 +1,22 @@
package io.gitlab.jfronny.muscript.test;
import io.gitlab.jfronny.muscript.compiler.Parser;
import org.junit.jupiter.api.*;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class ClosureTest {
@Test
void testScript() {
assertEquals(8, Parser.parseScript("function(2, 1) function(2, 2) function(2, 3)").run(makeArgs()).asNumber().getValue());
}
@Test
void testClosure() {
assertEquals(2, number("{->2}()"));
assertEquals(2, number("{n->n}(2)"));
assertEquals(2, number("{n->n()}({->2})"));
assertEquals(2, number("{->num = 2 num = num * 2 num = num - 2}()"));
}
}

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.muscript.test;
import org.junit.jupiter.api.*;
import static io.gitlab.jfronny.muscript.test.MuTestUtil.*;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class CombinationTest {

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.muscript.test;
import org.junit.jupiter.api.*;
import static io.gitlab.jfronny.muscript.test.MuTestUtil.*;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class ListTest {

View File

@ -18,10 +18,5 @@ class LocationalErrorTest {
1 | 15 + 'yes'
^---^-- Here""",
assertThrows(Parser.ParseException.class, () -> Parser.parse("15 + 'yes'")).error.toString());
assertEquals("""
Error at '=' (character 9): Unexpected character
1 | string = 'Value'
^-- Here""",
assertThrows(Parser.ParseException.class, () -> Parser.parse("string = 'Value'")).error.toString());
}
}

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.muscript.test;
import org.junit.jupiter.api.*;
import static io.gitlab.jfronny.muscript.test.MuTestUtil.*;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class NumberTest {

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.muscript.test;
import org.junit.jupiter.api.*;
import static io.gitlab.jfronny.muscript.test.MuTestUtil.*;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class ObjectTest {

View File

@ -2,7 +2,7 @@ package io.gitlab.jfronny.muscript.test;
import org.junit.jupiter.api.*;
import static io.gitlab.jfronny.muscript.test.MuTestUtil.*;
import static io.gitlab.jfronny.muscript.test.util.MuTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;
class StringTest {

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.muscript.test;
package io.gitlab.jfronny.muscript.test.util;
import io.gitlab.jfronny.muscript.data.dynamic.DObject;
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
import io.gitlab.jfronny.muscript.compiler.Parser;
import io.gitlab.jfronny.muscript.ast.Expr;
@ -32,7 +33,7 @@ public class MuTestUtil {
return Parser.parse(source).asStringExpr().get(makeArgs());
}
public static Dynamic<?> makeArgs() {
public static DObject makeArgs() {
return of(Map.of(
"boolean", of(true),
"number", of(15),

View File

@ -0,0 +1,10 @@
package io.gitlab.jfronny.muscript.test.util;
import io.gitlab.jfronny.muscript.data.Scope;
public class UnforkableScope extends Scope {
@Override
public Scope fork() {
return this;
}
}

View File

@ -0,0 +1,35 @@
package io.gitlab.jfronny.commons.data;
import io.gitlab.jfronny.commons.data.immutable.*;
import java.util.*;
public class ImmCollection {
public static <T> Collection<T> of(Collection<T> collection) {
return new ImmutableCollection<>(collection);
}
public static <T> List<T> of(List<T> list) {
return new ImmutableList<>(list);
}
public static <T> Set<T> of(Set<T> set) {
return new ImmutableSet<>(set);
}
public static <K, V> Map<K, V> of(Map<K, V> map) {
return new ImmutableMap<>(map);
}
public static <T> Iterable<T> of(Iterable<T> iterable) {
return new ImmutableIterable<>(iterable);
}
public static <T> Iterator<T> of(Iterator<T> iterator) {
return new ImmutableIterator<>(iterator);
}
public static <T> ListIterator<T> of(ListIterator<T> listIterator) {
return new ImmutableListIterator<>(listIterator);
}
}

View File

@ -0,0 +1,96 @@
package io.gitlab.jfronny.commons.data.immutable;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class ImmutableCollection<T, S extends Collection<T>> extends ImmutableIterable<T, S> implements Collection<T> {
public ImmutableCollection(S delegate) {
super(delegate);
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return delegate.contains(o);
}
@NotNull
@Override
public Object @NotNull [] toArray() {
return delegate.toArray();
}
@NotNull
@Override
public <T1> T1 @NotNull [] toArray(@NotNull T1 @NotNull [] t1s) {
return delegate.toArray(t1s);
}
@Override
public <T1> T1[] toArray(IntFunction<T1[]> generator) {
return delegate.toArray(generator);
}
@Override
public boolean add(T t) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(@NotNull Collection<?> collection) {
return delegate.containsAll(collection);
}
@Override
public boolean addAll(@NotNull Collection<? extends T> collection) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(@NotNull Collection<?> collection) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeIf(Predicate<? super T> filter) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(@NotNull Collection<?> collection) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Stream<T> stream() {
return delegate.stream();
}
@Override
public Stream<T> parallelStream() {
return delegate.parallelStream();
}
}

View File

@ -0,0 +1,30 @@
package io.gitlab.jfronny.commons.data.immutable;
import io.gitlab.jfronny.commons.data.ImmCollection;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.function.Consumer;
public class ImmutableIterable<T, S extends Iterable<T>> extends ImmutableObject<S> implements Iterable<T> {
public ImmutableIterable(S delegate) {
super(delegate);
}
@NotNull
@Override
public Iterator<T> iterator() {
return ImmCollection.of(delegate.iterator());
}
@Override
public void forEach(Consumer<? super T> action) {
delegate.forEach(action);
}
@Override
public Spliterator<T> spliterator() {
return delegate.spliterator();
}
}

View File

@ -0,0 +1,30 @@
package io.gitlab.jfronny.commons.data.immutable;
import java.util.Iterator;
import java.util.function.Consumer;
public class ImmutableIterator<T, S extends Iterator<T>> extends ImmutableObject<S> implements Iterator<T> {
public ImmutableIterator(S delegate) {
super(delegate);
}
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@Override
public T next() {
return delegate.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
delegate.forEachRemaining(action);
}
}

View File

@ -0,0 +1,76 @@
package io.gitlab.jfronny.commons.data.immutable;
import io.gitlab.jfronny.commons.data.ImmCollection;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.UnaryOperator;
public class ImmutableList<T, S extends List<T>> extends ImmutableCollection<T, S> implements List<T> {
public ImmutableList(S delegate) {
super(delegate);
}
@Override
public boolean addAll(int i, @NotNull Collection<? extends T> collection) {
throw new UnsupportedOperationException();
}
@Override
public void replaceAll(UnaryOperator<T> operator) {
throw new UnsupportedOperationException();
}
@Override
public void sort(Comparator<? super T> c) {
throw new UnsupportedOperationException();
}
@Override
public T get(int i) {
return delegate.get(i);
}
@Override
public T set(int i, T t) {
throw new UnsupportedOperationException();
}
@Override
public void add(int i, T t) {
throw new UnsupportedOperationException();
}
@Override
public T remove(int i) {
throw new UnsupportedOperationException();
}
@Override
public int indexOf(Object o) {
return delegate.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return delegate.lastIndexOf(o);
}
@NotNull
@Override
public ListIterator<T> listIterator() {
return ImmCollection.of(delegate.listIterator());
}
@NotNull
@Override
public ListIterator<T> listIterator(int i) {
return ImmCollection.of(delegate.listIterator(i));
}
@NotNull
@Override
public List<T> subList(int i, int i1) {
return ImmCollection.of(delegate.subList(i, i1));
}
}

View File

@ -0,0 +1,39 @@
package io.gitlab.jfronny.commons.data.immutable;
import java.util.ListIterator;
public class ImmutableListIterator<T, S extends ListIterator<T>> extends ImmutableIterator<T, S> implements ListIterator<T> {
public ImmutableListIterator(S delegate) {
super(delegate);
}
@Override
public boolean hasPrevious() {
return delegate.hasPrevious();
}
@Override
public T previous() {
return delegate.previous();
}
@Override
public int nextIndex() {
return delegate.nextIndex();
}
@Override
public int previousIndex() {
return delegate.previousIndex();
}
@Override
public void set(T t) {
throw new UnsupportedOperationException();
}
@Override
public void add(T t) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,137 @@
package io.gitlab.jfronny.commons.data.immutable;
import io.gitlab.jfronny.commons.data.ImmCollection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.*;
public class ImmutableMap<K, V> extends ImmutableObject<Map<K, V>> implements Map<K, V> {
public ImmutableMap(Map<K, V> delegate) {
super(delegate);
}
@Override
public int size() {
return delegate.size();
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return delegate.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return delegate.containsValue(o);
}
@Override
public V get(Object o) {
return delegate.get(o);
}
@Nullable
@Override
public V put(K k, V v) {
throw new UnsupportedOperationException();
}
@Override
public V remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(@NotNull Map<? extends K, ? extends V> map) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<K> keySet() {
return ImmCollection.of(delegate.keySet());
}
@NotNull
@Override
public Collection<V> values() {
return ImmCollection.of(delegate.values());
}
@NotNull
@Override
public Set<Entry<K, V>> entrySet() {
return ImmCollection.of(delegate.entrySet());
}
@Override
public V getOrDefault(Object key, V defaultValue) {
return delegate.getOrDefault(key, defaultValue);
}
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
delegate.forEach(action);
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public V putIfAbsent(K key, V value) {
if (containsKey(key)) return get(key);
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public V replace(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public V computeIfAbsent(K key, @NotNull Function<? super K, ? extends V> mappingFunction) {
if (containsKey(key)) return get(key);
throw new UnsupportedOperationException();
}
@Override
public V computeIfPresent(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V compute(K key, @NotNull BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V merge(K key, @NotNull V value, @NotNull BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,24 @@
package io.gitlab.jfronny.commons.data.immutable;
public class ImmutableObject<S> {
protected final S delegate;
public ImmutableObject(S delegate) {
this.delegate = delegate;
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof ImmutableObject<?> ob ? delegate.equals(ob.delegate) : delegate.equals(obj);
}
@Override
public String toString() {
return delegate.toString();
}
}

View File

@ -0,0 +1,15 @@
package io.gitlab.jfronny.commons.data.immutable;
import java.util.Set;
public class ImmutableSet<T, S extends Set<T>> extends ImmutableCollection<T, S> implements Set<T> {
public static class Simple<T> extends ImmutableSet<T, Set<T>> {
public Simple(Set<T> delegate) {
super(delegate);
}
}
public ImmutableSet(S delegate) {
super(delegate);
}
}