Support for assignment operators and closures
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
This commit is contained in:
parent
c8f6b0b6c4
commit
2847e05440
@ -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)
|
||||
|
@ -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())))
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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!");
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.");
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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}()"));
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user