feat(muscript): Weak type signature system for Dynamic, might get extended to muScript expressions
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
0c2db6fe08
commit
749b475d0d
|
@ -42,7 +42,7 @@ public class Call extends DynamicExpr {
|
|||
throw new LocationalException(location, "Could not perform call successfully", e);
|
||||
}
|
||||
try {
|
||||
return dc.getValue().apply(arg);
|
||||
return dc.call(arg);
|
||||
} catch (LocationalException le) {
|
||||
throw le.appendStack(new StackFrame.Raw(location.file(), dc.getName(), left.location.chStart()));
|
||||
} catch (DynamicTypeConversionException e) {
|
||||
|
|
|
@ -6,9 +6,11 @@ import io.gitlab.jfronny.muscript.compiler.*;
|
|||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DTypeCallable;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -31,9 +33,18 @@ public class Closure extends DynamicExpr {
|
|||
this.variadic = variadic;
|
||||
}
|
||||
|
||||
private DTypeCallable getSignature() {
|
||||
//TODO take into account inner instructions to properly determine type
|
||||
List<DTypeCallable.Arg> args = new LinkedList<>();
|
||||
for (int i = 0; i < boundArgs.size(); i++) {
|
||||
args.add(new DTypeCallable.Arg(boundArgs.get(i), null, variadic && (i == boundArgs.size() - 1)));
|
||||
}
|
||||
return new DTypeCallable(args, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DCallable get(Scope dataRoot) {
|
||||
return DFinal.of(args -> {
|
||||
return DFinal.of(getSignature(), args -> {
|
||||
// Compare with ExprGroup.get()
|
||||
int ac = args.size();
|
||||
int ae = boundArgs.size();
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.gitlab.jfronny.muscript.data;
|
|||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -79,12 +80,24 @@ public class Scope implements DObject {
|
|||
return set(key, DFinal.of(value));
|
||||
}
|
||||
|
||||
public Scope set(String key, DTypeObject signature, Map<String, ? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(signature, value));
|
||||
}
|
||||
|
||||
public Scope set(String key, List<? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(value));
|
||||
}
|
||||
|
||||
public Scope set(String key, DTypeList signature, List<? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(signature, value));
|
||||
}
|
||||
|
||||
public Scope set(String key, Function<DList, ? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(value, () -> key));
|
||||
return set(key, DFinal.of(value, () -> key, key));
|
||||
}
|
||||
|
||||
public Scope set(String key, DType signature, Function<DList, ? extends Dynamic> value) {
|
||||
return set(key, DFinal.of(signature, value, () -> key, key));
|
||||
}
|
||||
|
||||
public Scope fork() {
|
||||
|
|
|
@ -2,13 +2,24 @@ package io.gitlab.jfronny.muscript.data.dynamic;
|
|||
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.NamedDCallable;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public non-sealed interface DCallable extends Dynamic {
|
||||
String DEFAULT_NAME = "<unnamed>";
|
||||
|
||||
default Dynamic call(DList args) {
|
||||
DType signature = getSignature();
|
||||
if (signature != null) {
|
||||
List<DType> argTypes = args.getValue().stream().map(Dynamic::getSignature).toList();
|
||||
if (!TypeMatcher.match(signature, argTypes)) {
|
||||
List<DTypeCallable.Arg> extraArgs = argTypes.stream().map(s -> new DTypeCallable.Arg(null, s, false)).toList();
|
||||
DTypeCallable callSignature = new DTypeCallable(extraArgs, null);
|
||||
throw new IllegalArgumentException("Signature mismatch for " + getName() + ": expected <" + signature + "> but got <" + callSignature + ">");
|
||||
}
|
||||
}
|
||||
return getValue().apply(args);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ package io.gitlab.jfronny.muscript.data.dynamic;
|
|||
|
||||
import io.gitlab.jfronny.muscript.ast.NullLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DTypePrimitive;
|
||||
|
||||
public final class DNull implements Dynamic {
|
||||
@Override
|
||||
|
@ -14,6 +16,11 @@ public final class DNull implements Dynamic {
|
|||
return new NullLiteral(CodeLocation.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return DTypePrimitive.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.gitlab.jfronny.muscript.data.dynamic;
|
|||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.muscript.compiler.MuScriptVersion;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
import io.gitlab.jfronny.muscript.libs.StandardLib;
|
||||
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
|
@ -13,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a value of an unknown type
|
||||
|
@ -107,4 +109,15 @@ public sealed interface Dynamic permits DBool, DCallable, DList, DNull, DNumber,
|
|||
if (this instanceof DCallable callable) return callable;
|
||||
else throw new DynamicTypeConversionException("callable", this);
|
||||
}
|
||||
|
||||
default DType getSignature() {
|
||||
Set<DType> variants = new HashSet<>();
|
||||
if (isBool()) variants.add(DTypePrimitive.BOOL);
|
||||
if (isNumber()) variants.add(DTypePrimitive.NUMBER);
|
||||
if (isString()) variants.add(DTypePrimitive.STRING);
|
||||
if (isObject()) variants.add(new DTypeObject(null));
|
||||
if (isList()) variants.add(new DTypeList(null));
|
||||
if (isCallable()) variants.add(new DTypeCallable(null, null));
|
||||
return variants.size() == 1 ? variants.stream().findFirst().orElseThrow() : new DTypeAnd(variants);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,21 @@ import io.gitlab.jfronny.muscript.ast.dynamic.*;
|
|||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DCallableLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public record DCallableObject(Map<String, ? extends Dynamic> value, DTypeObject objectSignature, DCallable callable) implements DObject {
|
||||
public DCallableObject(Map<String, ? extends Dynamic> value, DCallable callable) {
|
||||
this(value, new DTypeObject(null), callable);
|
||||
}
|
||||
|
||||
public DCallableObject {
|
||||
Objects.requireNonNull(value);
|
||||
Objects.requireNonNull(objectSignature);
|
||||
Objects.requireNonNull(callable);
|
||||
}
|
||||
|
||||
public record DCallableObject(Map<String, ? extends Dynamic> value, DCallable callable) implements DObject {
|
||||
@Override
|
||||
public Map<String, ? extends Dynamic> getValue() {
|
||||
return value;
|
||||
|
@ -30,6 +40,11 @@ public record DCallableObject(Map<String, ? extends Dynamic> value, DCallable ca
|
|||
return new Call(CodeLocation.NONE, new Bind(CodeLocation.NONE, new Variable(CodeLocation.NONE, "callableObject"), DObject.super.toExpr()), List.of(new Call.Arg(callable.toExpr().asDynamicExpr(), false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return new DTypeAnd(Set.of(objectSignature, callable.getSignature()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
|
|
|
@ -8,13 +8,18 @@ import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
|||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DNumberLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DStringLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.*;
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.NUMBER;
|
||||
|
||||
public record DDate(Supplier<LocalDate> date) implements DObject {
|
||||
public static final DType SIGNATURE = object(NUMBER).or(STRING).or(NUMBER);
|
||||
|
||||
@Override
|
||||
public Map<String, Dynamic> getValue() {
|
||||
return Map.of(
|
||||
|
@ -53,6 +58,11 @@ public record DDate(Supplier<LocalDate> date) implements DObject {
|
|||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return SIGNATURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return date.get().toString();
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DListLens;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public enum DEmpty implements DObject {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Dynamic> getValue() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DList asList() {
|
||||
return new DListLens(this, List::of);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import io.gitlab.jfronny.muscript.ast.literal.StringLiteral;
|
|||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -17,17 +18,25 @@ import java.util.*;
|
|||
* An enum represented as an OObject.
|
||||
* May also have a selected value with automatic conversions to a number (index) and string
|
||||
*/
|
||||
public record DEnum(Map<String, ? extends Dynamic> values, @Nullable DEnumEntry value) implements DObject {
|
||||
public record DEnum(Map<String, ? extends Dynamic> values, @Nullable DEnumEntry value, @Nullable DType valueSignature) implements DObject {
|
||||
public DEnum(Map<String, ? extends Dynamic> values) {
|
||||
this(values, (DEnumEntry) null);
|
||||
this(null, values);
|
||||
}
|
||||
|
||||
public DEnum(DType valueSignature, Map<String, ? extends Dynamic> values) {
|
||||
this(values, null, valueSignature);
|
||||
}
|
||||
|
||||
public DEnum(Map<String, ? extends Dynamic> values, @Nullable String value) {
|
||||
this(values, value == null ? null : new DEnumEntry(value, values.keySet().stream().toList().indexOf(value), true));
|
||||
this(null, values, value);
|
||||
}
|
||||
|
||||
public DEnum(DType valueSignature, Map<String, ? extends Dynamic> values, @Nullable String value) {
|
||||
this(values, value == null ? null : new DEnumEntry(value, values.keySet().stream().toList().indexOf(value), true), valueSignature);
|
||||
}
|
||||
|
||||
public DEnum(List<String> values, String value) {
|
||||
this(createMap(values, value), value == null ? null : new DEnumEntry(value, values.indexOf(value), true));
|
||||
this(createMap(values, value), value == null ? null : new DEnumEntry(value, values.indexOf(value), true), DTypePrimitive.STRING);
|
||||
}
|
||||
|
||||
public DEnum(List<String> values) {
|
||||
|
@ -61,12 +70,12 @@ public record DEnum(Map<String, ? extends Dynamic> values, @Nullable DEnumEntry
|
|||
|
||||
@Override
|
||||
public boolean isNumber() {
|
||||
return true;
|
||||
return value != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DNumber asNumber() {
|
||||
return value != null ? value.asNumber() : DObject.super.asNumber();
|
||||
return value != null ? new DNumberLens(this, value::index) : DObject.super.asNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,6 +86,16 @@ public record DEnum(Map<String, ? extends Dynamic> values, @Nullable DEnumEntry
|
|||
return new Call(CodeLocation.NONE, new Variable(CodeLocation.NONE, "enum"), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
Set<DType> types = new HashSet<>();
|
||||
types.add(new DTypeObject(valueSignature));
|
||||
types.add(new DTypeList(valueSignature));
|
||||
types.add(DTypePrimitive.STRING);
|
||||
if (value != null) types.add(DTypePrimitive.NUMBER);
|
||||
return new DTypeAnd(types);
|
||||
}
|
||||
|
||||
private static Map<String, Dynamic> createMap(List<String> values, String value) {
|
||||
Map<String, Dynamic> result = new LinkedHashMap<>();
|
||||
DEnumEntry v = new DEnumEntry(value, values.indexOf(value), true);
|
||||
|
|
|
@ -7,10 +7,9 @@ import io.gitlab.jfronny.muscript.ast.Expr;
|
|||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
import io.gitlab.jfronny.muscript.compiler.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -28,31 +27,59 @@ public class DFinal {
|
|||
}
|
||||
|
||||
public static DObject of(Map<String, ? extends Dynamic> b) {
|
||||
return new FObject(ImmCollection.copyOf((Map<String, Dynamic>) b));
|
||||
return of(new DTypeObject(null), b);
|
||||
}
|
||||
|
||||
public static DObject of(DTypeObject signature, Map<String, ? extends Dynamic> b) {
|
||||
return new FObject(ImmCollection.copyOf((Map<String, Dynamic>) b), Objects.requireNonNull(signature));
|
||||
}
|
||||
|
||||
public static DList of(Dynamic... b) {
|
||||
return new FList(List.of(b));
|
||||
return of(new DTypeList(null), b);
|
||||
}
|
||||
|
||||
public static DList of(DTypeList signature, Dynamic... b) {
|
||||
return new FList(List.of(b), Objects.requireNonNull(signature));
|
||||
}
|
||||
|
||||
public static DList of(List<? extends Dynamic> b) {
|
||||
return new FList(ImmCollection.copyOf((List<Dynamic>) b));
|
||||
return of(new DTypeList(null), b);
|
||||
}
|
||||
|
||||
public static DList of(DTypeList signature, List<? extends Dynamic> b) {
|
||||
return new FList(ImmCollection.copyOf((List<Dynamic>) b), Objects.requireNonNull(signature));
|
||||
}
|
||||
|
||||
public static DCallable of(Function<DList, ? extends Dynamic> b, Supplier<String> serialized) {
|
||||
return of(b, serialized, null);
|
||||
return of(new DTypeCallable(null, null), b, Objects.requireNonNull(serialized));
|
||||
}
|
||||
|
||||
public static DCallable of(DType signature, Function<DList, ? extends Dynamic> b, Supplier<String> serialized) {
|
||||
return of(Objects.requireNonNull(signature), b, serialized, null);
|
||||
}
|
||||
|
||||
public static DCallable of(Function<DList, ? extends Dynamic> b, String name) {
|
||||
return of(name, b, () -> new Variable(CodeLocation.NONE, name));
|
||||
return of(new DTypeCallable(null, null), b, name);
|
||||
}
|
||||
|
||||
public static DCallable of(DType signature, Function<DList, ? extends Dynamic> b, String name) {
|
||||
return of(Objects.requireNonNull(signature), name, b, () -> new Variable(CodeLocation.NONE, name));
|
||||
}
|
||||
|
||||
public static DCallable of(Function<DList, ? extends Dynamic> b, Supplier<String> serialized, String name) {
|
||||
return of(name, b, () -> Parser.parse(MuScriptVersion.DEFAULT, serialized.get()));
|
||||
return of(new DTypeCallable(null, null), b, serialized, name);
|
||||
}
|
||||
|
||||
public static DCallable of(DType signature, Function<DList, ? extends Dynamic> b, Supplier<String> serialized, String name) {
|
||||
return of(Objects.requireNonNull(signature), name, b, () -> Parser.parse(MuScriptVersion.DEFAULT, serialized.get()));
|
||||
}
|
||||
|
||||
public static DCallable of(String name, Function<DList, ? extends Dynamic> b, Supplier<Expr<?>> serialized) {
|
||||
return new FCallable((Function<DList, Dynamic>) b, new LazySupplier<>(serialized), name);
|
||||
return of(new DTypeCallable(null, null), name, b, serialized);
|
||||
}
|
||||
|
||||
public static DCallable of(DType signature, String name, Function<DList, ? extends Dynamic> b, Supplier<Expr<?>> serialized) {
|
||||
return new FCallable((Function<DList, Dynamic>) b, new LazySupplier<>(serialized), name, Objects.requireNonNull(signature));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,6 +98,11 @@ public class DFinal {
|
|||
public String toString() {
|
||||
return StringFormatter.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return DTypePrimitive.BOOL;
|
||||
}
|
||||
}
|
||||
|
||||
private record FNumber(double value) implements DNumber, FImpl {
|
||||
|
@ -83,6 +115,11 @@ public class DFinal {
|
|||
public String toString() {
|
||||
return StringFormatter.toStringPrecise(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return DTypePrimitive.NUMBER;
|
||||
}
|
||||
}
|
||||
|
||||
private record FString(String value) implements DString, FImpl {
|
||||
|
@ -95,9 +132,14 @@ public class DFinal {
|
|||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return DTypePrimitive.STRING;
|
||||
}
|
||||
}
|
||||
|
||||
private record FObject(Map<String, Dynamic> value) implements DObject, FImpl {
|
||||
private record FObject(Map<String, Dynamic> value, DTypeObject signature) implements DObject, FImpl {
|
||||
@Override
|
||||
public Map<String, Dynamic> getValue() {
|
||||
return value;
|
||||
|
@ -107,9 +149,14 @@ public class DFinal {
|
|||
public String toString() {
|
||||
return Dynamic.serialize(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
||||
private record FList(List<Dynamic> value) implements DList, FImpl {
|
||||
private record FList(List<Dynamic> value, DTypeList signature) implements DList, FImpl {
|
||||
@Override
|
||||
public List<Dynamic> getValue() {
|
||||
return value;
|
||||
|
@ -119,9 +166,14 @@ public class DFinal {
|
|||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
||||
private record FCallable(Function<DList, Dynamic> value, Supplier<Expr<?>> gen, String name) implements DCallable, FImpl {
|
||||
private record FCallable(Function<DList, Dynamic> value, Supplier<Expr<?>> gen, String name, DType signature) implements DCallable, FImpl {
|
||||
@Override
|
||||
public Expr<?> toExpr() {
|
||||
return gen.get();
|
||||
|
@ -144,7 +196,12 @@ public class DFinal {
|
|||
|
||||
@Override
|
||||
public DCallable named(String name) {
|
||||
return new FCallable(value, gen, name);
|
||||
return new FCallable(value, gen, name, signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,18 +5,20 @@ import io.gitlab.jfronny.muscript.ast.dynamic.Call;
|
|||
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
|
||||
import io.gitlab.jfronny.muscript.ast.literal.NumberLiteral;
|
||||
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DNumberLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.lens.DStringLens;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.*;
|
||||
|
||||
public record DTime(Supplier<LocalTime> time) implements DObject {
|
||||
public static final DType SIGNATURE = object(NUMBER).or(STRING).or(NUMBER);
|
||||
|
||||
@Override
|
||||
public Map<String, Dynamic> getValue() {
|
||||
return Map.of(
|
||||
|
@ -55,6 +57,11 @@ public record DTime(Supplier<LocalTime> time) implements DObject {
|
|||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return SIGNATURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return time.get().toString();
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
|||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -83,4 +84,9 @@ public interface DelegateDynamic extends DynamicBase {
|
|||
default DCallable asCallable() {
|
||||
return getDelegate().asCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DType getSignature() {
|
||||
return getDelegate().getSignature();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.gitlab.jfronny.muscript.data.dynamic.additional;
|
|||
|
||||
import io.gitlab.jfronny.muscript.ast.Expr;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -25,4 +26,9 @@ public record NamedDCallable(DCallable inner, String name) implements DCallable
|
|||
public Function<DList, Dynamic> getValue() {
|
||||
return inner.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DType getSignature() {
|
||||
return inner.getSignature();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package io.gitlab.jfronny.muscript.data.dynamic.lens;
|
|||
|
||||
import io.gitlab.jfronny.muscript.compiler.ExprWriter;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
@ -78,6 +80,11 @@ public abstract sealed class DLens implements DynamicBase permits DBoolLens, DCa
|
|||
return source.asCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DType getSignature() {
|
||||
return source.getSignature();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return source.hashCode();
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DSL {
|
||||
public static final DTypePrimitive BOOL = DTypePrimitive.BOOL;
|
||||
public static final DTypePrimitive STRING = DTypePrimitive.STRING;
|
||||
public static final DTypePrimitive NUMBER = DTypePrimitive.NUMBER;
|
||||
public static final DTypePrimitive NULL = DTypePrimitive.NULL;
|
||||
|
||||
public static DTypeGeneric generic(int index) {
|
||||
return new DTypeGeneric(index);
|
||||
}
|
||||
|
||||
public static DTypeList list(@Nullable DType entryType) {
|
||||
return new DTypeList(entryType);
|
||||
}
|
||||
|
||||
public static DTypeObject object(@Nullable DType entryType) {
|
||||
return new DTypeObject(entryType);
|
||||
}
|
||||
|
||||
public static DTypeCallable callable(@Nullable List<DTypeCallable.Arg> from, @Nullable DType to) {
|
||||
return new DTypeCallable(from, to);
|
||||
}
|
||||
|
||||
public static DTypeCallable callable(@Nullable DType to, DTypeCallable.Arg... from) {
|
||||
return new DTypeCallable(List.of(from), to);
|
||||
}
|
||||
|
||||
public static DTypeCallable.Arg arg(@Nullable String name, @Nullable DType type) {
|
||||
return new DTypeCallable.Arg(name, type, false);
|
||||
}
|
||||
|
||||
public static DTypeCallable.Arg arg(@Nullable String name, @Nullable DType type, boolean variadic) {
|
||||
return new DTypeCallable.Arg(name, type, variadic);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public sealed interface DType permits DTypeCallable, DTypeGeneric, DTypeList, DTypeObject, DTypePrimitive, DTypeSum, DTypeAnd {
|
||||
static String toString(@Nullable DType type) {
|
||||
return toString(type, false);
|
||||
}
|
||||
|
||||
default DType or(DType alternative) {
|
||||
return new DTypeSum(Set.of(this, alternative));
|
||||
}
|
||||
|
||||
default DType and(DType additional) {
|
||||
return new DTypeAnd(Set.of(this, additional));
|
||||
}
|
||||
|
||||
static String toString(@Nullable DType type, boolean wrapComplex) {
|
||||
//TODO replace with switch pattern
|
||||
if (type == null) return "any";
|
||||
else if (type instanceof DTypePrimitive t) return switch (t) {
|
||||
case BOOL -> "bool";
|
||||
case NUMBER -> "number";
|
||||
case STRING -> "string";
|
||||
case NULL -> "null";
|
||||
};
|
||||
else if (type instanceof DTypeGeneric t) return "T" + t.index();
|
||||
else if (type instanceof DTypeList t) return "[" + toString(t.entryType(), false) + "]";
|
||||
else if (type instanceof DTypeObject t) return "{" + toString(t.entryType(), false) + "}";
|
||||
else if (type instanceof DTypeCallable t) {
|
||||
String args = t.from() == null
|
||||
? "any"
|
||||
: "(" + t.from().stream().map(Objects::toString).collect(Collectors.joining(", ")) + ")";
|
||||
return args + " -> " + toString(t.to(), true);
|
||||
} else if (type instanceof DTypeSum t) return (wrapComplex ? "<" : "") + t.elements().stream().map(s -> toString(s, true)).collect(Collectors.joining(" | ")) + (wrapComplex ? ">" : "");
|
||||
else if (type instanceof DTypeAnd t) return (wrapComplex ? "<" : "") + t.elements().stream().map(s -> toString(s, true)).collect(Collectors.joining(" & ")) + (wrapComplex ? ">" : "");
|
||||
else throw new IllegalArgumentException("Unexpected DType implementation: " + type.getClass());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public record DTypeAnd(Set<DType> elements) implements DType {
|
||||
public DTypeAnd(Set<DType> elements) {
|
||||
if (elements.isEmpty()) throw new IllegalArgumentException("Cannot create union type without elements");
|
||||
|
||||
Set<DType> simple = new LinkedHashSet<>();
|
||||
Set<DType> list = new LinkedHashSet<>();
|
||||
Set<DType> object = new LinkedHashSet<>();
|
||||
|
||||
boolean foundNullList = false, foundNullObject = false;
|
||||
|
||||
Queue<DType> toProcess = new LinkedList<>(elements);
|
||||
while (!toProcess.isEmpty()) {
|
||||
DType type = toProcess.remove();
|
||||
//TODO replace with pattern match
|
||||
if (type instanceof DTypePrimitive || type instanceof DTypeSum || type instanceof DTypeCallable || type instanceof DTypeGeneric) simple.add(type);
|
||||
else if (type instanceof DTypeList u) {
|
||||
if (u.entryType() == null) {
|
||||
if (!foundNullList) simple.add(new DTypeList(null));
|
||||
foundNullList = true;
|
||||
} else list.add(u.entryType());
|
||||
} else if (type instanceof DTypeObject u) {
|
||||
if (u.entryType() == null) {
|
||||
if (!foundNullObject) simple.add(new DTypeObject(null));
|
||||
foundNullObject = true;
|
||||
} else object.add(u.entryType());
|
||||
} else if (type instanceof DTypeAnd u) toProcess.addAll(u.elements);
|
||||
else throw new IllegalArgumentException("Unexpected DType implementation: " + type.getClass());
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) simple.add(new DTypeList(new DTypeAnd(list)));
|
||||
if (!object.isEmpty()) simple.add(new DTypeObject(new DTypeAnd(object)));
|
||||
this.elements = ImmCollection.copyOf(simple);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record DTypeCallable(@Nullable List<Arg> from, @Nullable DType to) implements DType {
|
||||
public DTypeCallable {
|
||||
if (from != null) {
|
||||
for (Arg arg : from) {
|
||||
if (arg == null) throw new IllegalArgumentException("Contents of 'from' cannot be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
|
||||
public record Arg(@Nullable String name, @Nullable DType type, boolean variadic) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return (name == null ? DType.toString(type) : name + ": " + DType.toString(type)) + (variadic ? "..." : "");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
public record DTypeGeneric(int index) implements DType {
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record DTypeList(@Nullable DType entryType) implements DType {
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
//TODO redesign for complex objects
|
||||
public record DTypeObject(@Nullable DType entryType) implements DType {
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
public enum DTypePrimitive implements DType {
|
||||
BOOL, NUMBER, STRING, NULL;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import io.gitlab.jfronny.commons.data.ImmCollection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public record DTypeSum(Set<DType> elements) implements DType {
|
||||
public DTypeSum(Set<DType> elements) {
|
||||
if (elements.isEmpty()) throw new IllegalArgumentException("Cannot create sum type without elements");
|
||||
|
||||
Set<DType> simple = new LinkedHashSet<>();
|
||||
Set<DType> list = new LinkedHashSet<>();
|
||||
Set<DType> object = new LinkedHashSet<>();
|
||||
|
||||
Queue<DType> toProcess = new LinkedList<>(elements);
|
||||
while (!toProcess.isEmpty()) {
|
||||
DType type = toProcess.remove();
|
||||
//TODO replace with pattern match
|
||||
if (type instanceof DTypePrimitive || type instanceof DTypeAnd || type instanceof DTypeCallable) simple.add(type);
|
||||
else if (type instanceof DTypeList u) list.add(u.entryType());
|
||||
else if (type instanceof DTypeObject u) object.add(u.entryType());
|
||||
else if (type instanceof DTypeSum u) toProcess.addAll(u.elements);
|
||||
else throw new IllegalArgumentException("Unexpected DType implementation: " + type.getClass());
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) simple.add(new DTypeList(list.contains(null) ? null : new DTypeSum(list)));
|
||||
if (!object.isEmpty()) simple.add(new DTypeObject(object.contains(null) ? null : new DTypeSum(object)));
|
||||
this.elements = ImmCollection.copyOf(simple);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DType.toString(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
package io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class TypeMatcher {
|
||||
public static boolean match(DType fn, List<DType> args) {
|
||||
if (fn instanceof DTypeAnd ds) {
|
||||
for (DType element : ds.elements()) {
|
||||
if (match(element, args)) return true;
|
||||
}
|
||||
return false;
|
||||
} else if (fn instanceof DTypeCallable ds) {
|
||||
return match(ds, args);
|
||||
} else return false; // Callable may have multiple other representations
|
||||
}
|
||||
|
||||
public static boolean match(DTypeCallable fn, List<DType> args) {
|
||||
return fn.from() == null || match(fn.from(), args);
|
||||
}
|
||||
|
||||
public static boolean match(List<DTypeCallable.Arg> declared, List<DType> provided) {
|
||||
return match(declared, provided, new MatchScope());
|
||||
}
|
||||
|
||||
private static boolean match(List<DTypeCallable.Arg> declared, List<DType> provided, MatchScope scope) {
|
||||
if (declared.isEmpty()) return provided.isEmpty();
|
||||
if (provided.isEmpty()) return declared.stream().allMatch(DTypeCallable.Arg::variadic);
|
||||
if (!declared.get(0).variadic()) {
|
||||
DType ct = declared.get(0).type();
|
||||
DType pt = provided.get(0);
|
||||
declared = declared.subList(1, declared.size());
|
||||
provided = provided.subList(1, provided.size());
|
||||
if (ct == null) return match(declared, provided, scope);
|
||||
Result r = match(ct, pt, scope);
|
||||
if (r == Result.MATCH) return match(declared, provided, scope);
|
||||
if (r == Result.FAILED) return false;
|
||||
for (Map<Integer, DType> s2 : ((Result.PossibleMatch) r).possibleHydrations()) {
|
||||
if (match(declared, provided, scope.fork(s2))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
DType cArg = declared.get(0).type();
|
||||
List<DTypeCallable.Arg> nextDeclared = declared.subList(1, declared.size());
|
||||
while (!provided.isEmpty()) {
|
||||
if (match(nextDeclared, provided, scope)) return true;
|
||||
DType dt = provided.get(0);
|
||||
provided = provided.subList(1, provided.size());
|
||||
if (cArg == null) continue;
|
||||
Result r = match(cArg, dt, scope);
|
||||
if (r == Result.FAILED) {
|
||||
match(cArg, dt, scope);
|
||||
return false;
|
||||
}
|
||||
if (r == Result.MATCH) continue;
|
||||
for (Map<Integer, DType> s2 : ((Result.PossibleMatch) r).possibleHydrations()) {
|
||||
if (match(declared, provided, scope.fork(s2))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return match(nextDeclared, provided, scope);
|
||||
}
|
||||
|
||||
private static Result match(DType declared, @Nullable DType provided, MatchScope scope) {
|
||||
if (declared instanceof DTypeGeneric dg) {
|
||||
if (scope.hydrations.containsKey(dg.index())) {
|
||||
Result r = match(scope.hydrations.get(dg.index()), provided, scope);
|
||||
if (r == Result.FAILED) {
|
||||
if (provided != null)
|
||||
return new Result.PossibleMatch(Set.of(Map.of(dg.index(), scope.hydrations.get(dg.index()).or(provided))));
|
||||
return Result.MATCH;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
// Ideally, we would actually store our null value, but Map doesn't support that and this works for now
|
||||
return new Result.PossibleMatch(provided == null ? Set.of(Map.of()) : Set.of(Map.of(dg.index(), provided)));
|
||||
} else if (provided instanceof DTypeSum dc) {
|
||||
Result currentResult = Result.MATCH;
|
||||
for (DType element : dc.elements()) {
|
||||
currentResult = currentResult.and(match(declared, element, scope), scope);
|
||||
}
|
||||
return currentResult;
|
||||
} else if (provided instanceof DTypeAnd dc && !(declared instanceof DTypeAnd)) {
|
||||
Result currentResult = Result.FAILED;
|
||||
for (DType element : dc.elements()) {
|
||||
currentResult = currentResult.or(match(declared, element, scope), scope);
|
||||
}
|
||||
return currentResult;
|
||||
} else if (declared instanceof DTypeCallable dg) {
|
||||
if (!(provided instanceof DTypeCallable dc)) return Result.FAILED;
|
||||
Result currentResult = Result.MATCH;
|
||||
if (dg.from() != null) {
|
||||
if (dc.from() == null) return Result.MATCH;
|
||||
List<DTypeCallable.Arg> dgf = dg.from();
|
||||
List<DTypeCallable.Arg> dcf = dc.from();
|
||||
// This would theoretically need more logic to properly test whether the functions match
|
||||
// but for now this bypass will do
|
||||
// if (dgf.size() != dcf.size()) return Result.FAILED;
|
||||
// for (int i = 0; i < dgf.size(); i++) {
|
||||
// if (dgf.get(i).variadic() != dcf.get(i).variadic()) return Result.FAILED;
|
||||
// currentResult = currentResult.and(match(dgf.get(i).type(), dcf.get(i).type(), scope), scope);
|
||||
// if (currentResult == Result.FAILED) return Result.FAILED;
|
||||
// }
|
||||
}
|
||||
if (dg.to() == null) return currentResult;
|
||||
if (dc.to() == null) return currentResult; // Temporary workaround: muScript closures always return unidentifiable types for now
|
||||
return currentResult.and(match(dg.to(), dc.to(), scope), scope);
|
||||
} else if (declared instanceof DTypeList dg) {
|
||||
if (!(provided instanceof DTypeList dc)) return Result.FAILED;
|
||||
if (dg.entryType() == null) return Result.MATCH;
|
||||
return match(dg.entryType(), dc.entryType(), scope);
|
||||
} else if (declared instanceof DTypeObject dg) {
|
||||
if (!(provided instanceof DTypeObject dc)) return Result.FAILED;
|
||||
if (dg.entryType() == null) return Result.MATCH;
|
||||
return match(dg.entryType(), dc.entryType(), scope);
|
||||
} else if (declared instanceof DTypePrimitive dg) {
|
||||
if (!(provided instanceof DTypePrimitive dc)) return Result.FAILED;
|
||||
return dg == dc ? Result.MATCH : Result.FAILED;
|
||||
} else if (declared instanceof DTypeSum dg) {
|
||||
Result currentResult = Result.FAILED;
|
||||
for (DType element : dg.elements()) {
|
||||
currentResult = currentResult.or(match(element, provided, scope), scope);
|
||||
}
|
||||
return currentResult;
|
||||
} else if (declared instanceof DTypeAnd dg) {
|
||||
Result currentResult = Result.MATCH;
|
||||
for (DType element : dg.elements()) {
|
||||
currentResult = currentResult.and(match(element, provided, scope), scope);
|
||||
}
|
||||
return currentResult;
|
||||
} else throw new IllegalArgumentException("Unexpected DType implementation: " + declared.getClass());
|
||||
}
|
||||
|
||||
private record MatchScope(Map<Integer, DType> hydrations) {
|
||||
public MatchScope() {
|
||||
this(Map.of());
|
||||
}
|
||||
|
||||
public MatchScope fork(Map<Integer, DType> additional) {
|
||||
Map<Integer, DType> fork = new LinkedHashMap<>(hydrations);
|
||||
fork.putAll(additional);
|
||||
return new MatchScope(Map.copyOf(fork));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed interface Result {
|
||||
default Result and(Result other, MatchScope scope) {
|
||||
if (other instanceof PossibleMatch pm) return pm.and(this, scope);
|
||||
if (other == FAILED) return FAILED;
|
||||
if (other == MATCH) return this;
|
||||
throw new IllegalArgumentException("Unexpected Result implementation");
|
||||
}
|
||||
|
||||
default Result or(Result other, MatchScope scope) {
|
||||
if (other instanceof PossibleMatch pm) return pm.and(this, scope);
|
||||
if (other == FAILED) return this;
|
||||
if (other == MATCH) return MATCH;
|
||||
throw new IllegalArgumentException("Unexpected Result implementation");
|
||||
}
|
||||
|
||||
Const FAILED = Const.FAILED;
|
||||
Const MATCH = Const.MATCH;
|
||||
enum Const implements Result { FAILED, MATCH }
|
||||
record PossibleMatch(Set<Map<Integer, DType>> possibleHydrations) implements Result {
|
||||
@Override
|
||||
public Result and(Result result, MatchScope scope) {
|
||||
if (result == FAILED) return FAILED;
|
||||
if (result == MATCH) return this;
|
||||
Set<Map<Integer, DType>> neu = ((PossibleMatch) result).possibleHydrations.stream().flatMap(left -> possibleHydrations.stream().filter(right ->
|
||||
Stream.concat(left.keySet().stream(), right.keySet().stream())
|
||||
.filter(left::containsKey)
|
||||
.filter(right::containsKey)
|
||||
.allMatch(s -> {
|
||||
Result r = match(left.get(s), right.get(s), scope);
|
||||
if (r == FAILED) return false;
|
||||
if (r == MATCH) return true;
|
||||
throw new IllegalArgumentException("Unexpected post-match Result implementation");
|
||||
})
|
||||
)).collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
if (neu.isEmpty()) return FAILED;
|
||||
return new PossibleMatch(neu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result or(Result other, MatchScope scope) {
|
||||
if (other == FAILED) return this;
|
||||
Stream<Map<Integer, DType>> oth = other == MATCH ? Stream.of(Map.of()) : ((PossibleMatch) other).possibleHydrations.stream();
|
||||
return new PossibleMatch(Stream.concat(possibleHydrations.stream(), oth).collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,14 +9,16 @@ import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.*;
|
||||
|
||||
public class IOLib {
|
||||
public static Scope addTo(MuScriptVersion version, Scope scope, IOWrapper io) {
|
||||
if (version.contains(MuScriptVersion.V3)) {
|
||||
scope.set("readString", args -> {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for readString: expected 1 but got " + args.size());
|
||||
scope.set("readString", callable(STRING, arg("fileName", STRING)), args -> {
|
||||
if (args.size() != 1) throw new SignatureDesyncException("readString");
|
||||
return DFinal.of(io.readString(args.get(0).asString().getValue()));
|
||||
}).set("runScript", args -> {
|
||||
if (args.isEmpty()) throw new IllegalArgumentException("Invalid number of arguments for evaluateScript: expected 1 or more but got " + args.size());
|
||||
}).set("runScript", callable(null, arg("fileName", STRING), arg("args", null, true)), args -> {
|
||||
if (args.isEmpty()) throw new SignatureDesyncException("runScript");
|
||||
Script script = io.parseScript(args.get(0).asString().getValue());
|
||||
List<? extends Dynamic> l = args.getValue();
|
||||
DList innerArgs = DFinal.of(l.subList(1, l.size()));
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package io.gitlab.jfronny.muscript.libs;
|
||||
|
||||
public class SignatureDesyncException extends IllegalArgumentException {
|
||||
public SignatureDesyncException(String method) {
|
||||
super("Signature desync detected for method: " + method + " (invoked with illegal arguments but passed automatic check)");
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import io.gitlab.jfronny.muscript.compiler.MuScriptVersion;
|
|||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.*;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.type.DType;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
@ -18,6 +19,7 @@ import java.util.function.Supplier;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal.of;
|
||||
import static io.gitlab.jfronny.muscript.data.dynamic.type.DSL.*;
|
||||
|
||||
public class StandardLib {
|
||||
private static final Random rnd = new Random();
|
||||
|
@ -33,10 +35,12 @@ public class StandardLib {
|
|||
.set("E", Math.E)
|
||||
.set("date", new DCallableObject(Map.of(
|
||||
"today", new DDate(LocalDate::now)
|
||||
), DFinal.of(args -> {
|
||||
), object(DDate.SIGNATURE), DFinal.of(callable(DDate.SIGNATURE, arg("epochDay", NUMBER))
|
||||
.and(callable(DDate.SIGNATURE, arg("year", NUMBER), arg("month", NUMBER), arg("day", NUMBER))),
|
||||
args -> {
|
||||
// Constructor
|
||||
if (args.size() == 1) return new DDate(() -> LocalDate.ofEpochDay(args.get(0).asNumber().getValue().longValue()));
|
||||
if (args.size() != 3) throw new IllegalArgumentException("Expected 3 arguments for full date constructor");
|
||||
if (args.size() != 3) throw new SignatureDesyncException("date");
|
||||
int a0 = args.get(0).asNumber().getValue().intValue();
|
||||
int a1 = args.get(1).asNumber().getValue().intValue();
|
||||
int a2 = args.get(2).asNumber().getValue().intValue();
|
||||
|
@ -44,104 +48,138 @@ public class StandardLib {
|
|||
}, "date")))
|
||||
.set("time", new DCallableObject(Map.of(
|
||||
"now", new DTime(LocalTime::now)
|
||||
), DFinal.of(args -> {
|
||||
), object(DTime.SIGNATURE), DFinal.of(callable(DTime.SIGNATURE, arg("secondOfDay", NUMBER))
|
||||
.and(callable(DTime.SIGNATURE, arg("hour", NUMBER), arg("minute", NUMBER), arg("second", NUMBER))),
|
||||
args -> {
|
||||
// Constructor
|
||||
if (args.size() == 1) return new DTime(() -> LocalTime.ofSecondOfDay(args.get(0).asNumber().getValue().intValue()));
|
||||
if (args.size() != 3) throw new IllegalArgumentException("Expected 3 arguments for full time constructor");
|
||||
if (args.size() != 3) throw new SignatureDesyncException("time");
|
||||
int a0 = args.get(0).asNumber().getValue().intValue();
|
||||
int a1 = args.get(1).asNumber().getValue().intValue();
|
||||
int a2 = args.get(2).asNumber().getValue().intValue();
|
||||
return new DTime(() -> LocalTime.of(a0, a1, a2));
|
||||
}, "time")))
|
||||
|
||||
.set("round", StandardLib::round)
|
||||
.set("floor", StandardLib::floor)
|
||||
.set("ceil", StandardLib::ceil)
|
||||
.set("abs", StandardLib::abs)
|
||||
.set("random", StandardLib::random)
|
||||
.set("round", round, StandardLib::round)
|
||||
.set("floor", floor, StandardLib::floor)
|
||||
.set("ceil", ceil, StandardLib::ceil)
|
||||
.set("abs", abs, StandardLib::abs)
|
||||
.set("random", random, StandardLib::random)
|
||||
|
||||
.set("toUpper", StandardLib::toUpper)
|
||||
.set("toLower", StandardLib::toLower)
|
||||
.set("contains", StandardLib::contains)
|
||||
.set("replace", StandardLib::replace);
|
||||
.set("toUpper", toUpper, StandardLib::toUpper)
|
||||
.set("toLower", toLower, StandardLib::toLower)
|
||||
.set("contains", contains, StandardLib::contains)
|
||||
.set("replace", replace, StandardLib::replace);
|
||||
}
|
||||
if (version.contains(MuScriptVersion.V2)) {
|
||||
scope
|
||||
.set("listOf", StandardLib::listOf)
|
||||
.set("len", StandardLib::len)
|
||||
.set("isEmpty", StandardLib::isEmpty)
|
||||
.set("concat", StandardLib::concat)
|
||||
.set("filter", StandardLib::filter)
|
||||
.set("allMatch", StandardLib::allMatch)
|
||||
.set("anyMatch", StandardLib::anyMatch)
|
||||
.set("map", StandardLib::map)
|
||||
.set("flatMap", StandardLib::flatMap)
|
||||
.set("fold", StandardLib::fold)
|
||||
.set("forEach", StandardLib::forEach)
|
||||
.set("toObject", StandardLib::toObject)
|
||||
.set("listOf", listOf, StandardLib::listOf)
|
||||
.set("len", len, StandardLib::len)
|
||||
.set("isEmpty", isEmpty, StandardLib::isEmpty)
|
||||
.set("concat", concat, StandardLib::concat)
|
||||
.set("filter", filter, StandardLib::filter)
|
||||
.set("allMatch", allMatch, StandardLib::allMatch)
|
||||
.set("anyMatch", anyMatch, StandardLib::anyMatch)
|
||||
.set("map", map, StandardLib::map)
|
||||
.set("flatMap", flatMap, StandardLib::flatMap)
|
||||
.set("fold", fold, StandardLib::fold)
|
||||
.set("forEach", forEach, StandardLib::forEach)
|
||||
.set("toObject", toObject, StandardLib::toObject)
|
||||
|
||||
.set("callableObject", StandardLib::callableObject)
|
||||
.set("enum", StandardLib::enum_)
|
||||
.set("keys", StandardLib::keys)
|
||||
.set("values", StandardLib::values)
|
||||
.set("callableObject", callableObject, StandardLib::callableObject)
|
||||
.set("enum", enum_, StandardLib::enum_)
|
||||
.set("keys", keys, StandardLib::keys)
|
||||
.set("values", values, StandardLib::values)
|
||||
|
||||
.set("fail", StandardLib::fail)
|
||||
.set("try", StandardLib::try_);
|
||||
.set("fail", fail, StandardLib::fail)
|
||||
.set("try", try_, StandardLib::try_);
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
// Numbers
|
||||
public static DNumber round(DList args) {
|
||||
if (args.size() == 1) {
|
||||
return of(Math.round(args.get(0).asNumber().getValue()));
|
||||
} else if (args.size() == 2) {
|
||||
double x = Math.pow(10, (int) (double) args.get(1).asNumber().getValue());
|
||||
return of(Math.round(args.get(0).asNumber().getValue() * x) / x);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid number of arguments for round: expected 1 or 2 but got " + args.size());
|
||||
}
|
||||
public static Map<String, String> printSignatures(MuScriptVersion version) {
|
||||
return createScope(version).getValue().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, s -> s.getValue().getSignature().toString()));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(printSignatures(MuScriptVersion.DEFAULT).entrySet().stream().map(s -> s.getKey() + ": " + s.getValue()).collect(Collectors.joining("\n")));
|
||||
}
|
||||
|
||||
// Numbers
|
||||
private static final DType round = callable(NUMBER, arg("number", NUMBER))
|
||||
.and(callable(NUMBER, arg("number", NUMBER), arg("decimalPlace", NUMBER)));
|
||||
public static DNumber round(DList args) {
|
||||
return switch (args.size()) {
|
||||
case 1 -> of(Math.round(args.get(0).asNumber().getValue()));
|
||||
case 2 -> {
|
||||
double x = Math.pow(10, (int) (double) args.get(1).asNumber().getValue());
|
||||
yield of(Math.round(args.get(0).asNumber().getValue() * x) / x);
|
||||
}
|
||||
default -> throw new SignatureDesyncException("round");
|
||||
};
|
||||
}
|
||||
|
||||
private static final DType floor = callable(NUMBER, arg("number", NUMBER));
|
||||
public static DNumber floor(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for floor: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("floor");
|
||||
return of(Math.floor(args.get(0).asNumber().getValue()));
|
||||
}
|
||||
|
||||
private static final DType ceil = callable(NUMBER, arg("number", NUMBER));
|
||||
public static DNumber ceil(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for ceil: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("ceil");
|
||||
return of(Math.ceil(args.get(0).asNumber().getValue()));
|
||||
}
|
||||
|
||||
private static final DType abs = callable(NUMBER, arg("number", NUMBER));
|
||||
public static DNumber abs(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for abs: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("abs");
|
||||
return of(Math.abs(args.get(0).asNumber().getValue()));
|
||||
}
|
||||
|
||||
public static DNumber random(DList args) {
|
||||
if (args.isEmpty()) return of(rnd.nextDouble());
|
||||
else if (args.size() == 2) {
|
||||
double min = args.get(0).asNumber().getValue();
|
||||
double max = args.get(1).asNumber().getValue();
|
||||
return of(min + (max - min) * rnd.nextDouble());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid number of arguments for random: expected 0 or 2 but got " + args.size());
|
||||
private static final DType random = callable(NUMBER)
|
||||
.and(callable(NUMBER, arg("min", NUMBER), arg("max", NUMBER)))
|
||||
.and(callable(generic(0), arg("in", list(generic(0)))))
|
||||
.and(callable(object(generic(0)), arg("in", object(generic(0)))));
|
||||
public static Dynamic random(DList args) {
|
||||
return switch (args.size()) {
|
||||
case 0 -> of(rnd.nextDouble());
|
||||
case 2 -> {
|
||||
double min = args.get(0).asNumber().getValue();
|
||||
double max = args.get(1).asNumber().getValue();
|
||||
yield of(min + (max - min) * rnd.nextDouble());
|
||||
}
|
||||
case 1 -> {
|
||||
if (args.get(0).isList()) {
|
||||
List<? extends Dynamic> list = args.get(0).asList().getValue();
|
||||
yield list.get(rnd.nextInt(list.size()));
|
||||
} else if (args.get(0).isObject()) {
|
||||
var list = List.copyOf(args.get(0).asObject().getValue().entrySet());
|
||||
yield objectRepresentation(list.get(rnd.nextInt(list.size())));
|
||||
} else throw new SignatureDesyncException("random");
|
||||
}
|
||||
default -> throw new SignatureDesyncException("random");
|
||||
};
|
||||
}
|
||||
|
||||
// Strings
|
||||
private static final DType toUpper = callable(STRING, arg("from", STRING));
|
||||
public static DString toUpper(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for toUpper: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("toUpper");
|
||||
return of(args.get(0).asString().getValue().toUpperCase());
|
||||
}
|
||||
|
||||
private static final DType toLower = callable(STRING, arg("from", STRING));
|
||||
public static DString toLower(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for toLower: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("toLower");
|
||||
return of(args.get(0).asString().getValue().toLowerCase());
|
||||
}
|
||||
|
||||
private static final DType contains = callable(BOOL, arg("search", list(generic(0))), arg("entry", generic(0)))
|
||||
.and(callable(BOOL, arg("search", object(null)), arg("key", STRING)))
|
||||
.and(callable(BOOL, arg("search", STRING), arg("substring", STRING)));
|
||||
public static DBool contains(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for contains: expected 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new SignatureDesyncException("contains");
|
||||
Dynamic arg0 = args.get(0);
|
||||
Dynamic arg1 = args.get(1);
|
||||
boolean contained = false;
|
||||
|
@ -151,71 +189,106 @@ public class StandardLib {
|
|||
return of(contained);
|
||||
}
|
||||
|
||||
private static final DType replace = callable(STRING, arg("in", STRING), arg("target", STRING), arg("replacement", STRING));
|
||||
public static DString replace(DList args) {
|
||||
if (args.size() != 3) throw new IllegalArgumentException("Invalid number of arguments for replace: expected 3 but got " + args.size());
|
||||
if (args.size() != 3) throw new SignatureDesyncException("replace");
|
||||
return of(args.get(0).asString().getValue().replace(args.get(1).asString().getValue(), args.get(2).asString().getValue()));
|
||||
}
|
||||
|
||||
// Lists
|
||||
private static final DType listOf = callable(list(generic(0)), arg("entries", generic(0), true));
|
||||
public static DList listOf(DList args) {
|
||||
return args;
|
||||
}
|
||||
|
||||
private static final DType len = callable(NUMBER, arg("of", STRING.or(object(null)).or(list(null))));
|
||||
public static DNumber len(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for len: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("len");
|
||||
Dynamic arg0 = args.get(0);
|
||||
if (arg0.isString()) return of(arg0.asString().getValue().length());
|
||||
if (arg0.isObject()) return of(arg0.asObject().getValue().size());
|
||||
return of(args.get(0).asList().size());
|
||||
if (arg0.isList()) return of(arg0.asList().size());
|
||||
throw new SignatureDesyncException("len");
|
||||
}
|
||||
|
||||
private static final DType isEmpty = callable(BOOL, arg("collection", object(null).or(list(null)).or(STRING)));
|
||||
public static DBool isEmpty(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for isEmpty: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("isEmpty");
|
||||
Dynamic arg0 = args.get(0);
|
||||
if (arg0.isObject()) return of(arg0.asObject().getValue().isEmpty());
|
||||
return of(arg0.asList().isEmpty());
|
||||
if (arg0.isList()) return of(arg0.asList().isEmpty());
|
||||
if (arg0.isString()) return of(arg0.asString().getValue().isEmpty());
|
||||
throw new SignatureDesyncException("isEmpty");
|
||||
}
|
||||
|
||||
public static DList concat(DList args) {
|
||||
return of(args.getValue().stream().flatMap(s -> s.asList().getValue().stream()).toList());
|
||||
private static final DType concat = callable(list(generic(0)), arg("lists", list(generic(0)), true))
|
||||
.and(callable(object(generic(0)), arg("objects", generic(0), true)));
|
||||
public static Dynamic concat(DList args) {
|
||||
if (args.isEmpty()) return DEmpty.INSTANCE;
|
||||
if (args.get(0).isList()) return of(args.getValue().stream().flatMap(s -> s.asList().getValue().stream()).toList());
|
||||
if (args.get(0).isObject()) return of(args.getValue().stream().flatMap(s -> s.asObject().getValue().entrySet().stream()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
throw new SignatureDesyncException("concat");
|
||||
}
|
||||
|
||||
public static DList filter(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for filter: expected 2 but got " + args.size());
|
||||
private static final DType filter = callable(list(generic(0)), arg("list", list(generic(0))), arg("predicate", callable(BOOL, arg("current", generic(0)))))
|
||||
.and(callable(object(generic(0)), arg("object", object(generic(0))), arg("predicate", callable(BOOL, arg("key", STRING), arg("value", generic(0))))));
|
||||
public static Dynamic filter(DList args) {
|
||||
if (args.size() != 2) throw new SignatureDesyncException("filter");
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
return of(args.get(0).asList().getValue().stream().filter(a -> dc.call(a).asBool().getValue()).toList());
|
||||
if (args.get(0).isList()) return of(args.get(0).asList().getValue().stream().filter(a -> dc.call(a).asBool().getValue()).toList());
|
||||
if (args.get(0).isObject()) return of(args.get(0).asObject().getValue().entrySet().stream().filter(entry -> dc.call(of(entry.getKey()), entry.getValue()).asBool().getValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
throw new SignatureDesyncException("filter");
|
||||
}
|
||||
|
||||
private static final DType allMatch = callable(BOOL, arg("list", list(generic(0))), arg("predicate", callable(BOOL, arg("current", generic(0)))))
|
||||
.and(callable(BOOL, arg("object", object(generic(0))), arg("predicate", callable(BOOL, arg("key", STRING), arg("value", STRING)))));
|
||||
public static DBool allMatch(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for allMatch: expected 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new SignatureDesyncException("allMatch");
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
return of(args.get(0).asList().getValue().stream().allMatch(a -> dc.call(a).asBool().getValue()));
|
||||
if (args.get(0).isList()) return of(args.get(0).asList().getValue().stream().allMatch(a -> dc.call(a).asBool().getValue()));
|
||||
if (args.get(0).isObject()) return of(args.get(0).asObject().getValue().entrySet().stream().allMatch(a -> dc.call(of(a.getKey()), a.getValue()).asBool().getValue()));
|
||||
throw new SignatureDesyncException("allMatch");
|
||||
}
|
||||
|
||||
private static final DType anyMatch = callable(BOOL, arg("list", list(generic(0))), arg("predicate", callable(BOOL, arg("current", generic(0)))))
|
||||
.and(callable(BOOL, arg("object", object(generic(0))), arg("predicate", callable(BOOL, arg("key", STRING), arg("value", STRING)))));
|
||||
public static DBool anyMatch(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for anyMatch: expected 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new SignatureDesyncException("anyMatch");
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
return of(args.get(0).asList().getValue().stream().anyMatch(a -> dc.call(a).asBool().getValue()));
|
||||
if (args.get(0).isList()) return of(args.get(0).asList().getValue().stream().anyMatch(a -> dc.call(a).asBool().getValue()));
|
||||
if (args.get(0).isObject()) return of(args.get(0).asObject().getValue().entrySet().stream().anyMatch(a -> dc.call(of(a.getKey()), a.getValue()).asBool().getValue()));
|
||||
throw new SignatureDesyncException("anyMatch");
|
||||
}
|
||||
|
||||
public static DList map(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for map: expected 2 but got " + args.size());
|
||||
return of(args.get(0).asList().getValue().stream().map(args.get(1).asCallable()::call).toList());
|
||||
}
|
||||
|
||||
public static DList flatMap(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for flatMap: expected 2 but got " + args.size());
|
||||
private static final DType map = callable(list(generic(1)), arg("list", list(generic(0))), arg("mapper", callable(generic(1), arg("value", generic(0)))))
|
||||
.and(callable(object(generic(1)), arg("object", object(generic(0))), arg("mapper", callable(generic(1), arg("key", STRING), arg("value", generic(0))))));
|
||||
public static Dynamic map(DList args) {
|
||||
if (args.size() != 2) throw new SignatureDesyncException("map");
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
return of(args.get(0).asList().getValue().stream().flatMap(a -> dc.call(a).asList().getValue().stream()).toList());
|
||||
if (args.get(0).isList()) return of(args.get(0).asList().getValue().stream().map(dc::call).toList());
|
||||
if (args.get(0).isObject()) return of(args.get(0).asObject().getValue().entrySet().stream().map(e -> new Entry(e.getKey(), dc.call(of(e.getKey()), e.getValue()))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
throw new SignatureDesyncException("map");
|
||||
}
|
||||
|
||||
private static final DType flatMap = callable(list(generic(1)), arg("list", list(generic(0))), arg("mapper", callable(list(generic(1)), arg("value", generic(0)))))
|
||||
.and(callable(object(generic(1)), arg("object", object(generic(0))), arg("mapper", callable(object(generic(1)), arg("key", STRING), arg("value", generic(0))))));
|
||||
public static Dynamic flatMap(DList args) {
|
||||
if (args.size() != 2) throw new SignatureDesyncException("flatMap");
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
if (args.get(0).isList()) return of(args.get(0).asList().getValue().stream().flatMap(a -> dc.call(a).asList().getValue().stream()).toList());
|
||||
if (args.get(0).isObject()) return of(args.get(0).asObject().getValue().entrySet().stream().flatMap(e -> dc.call(of(e.getKey()), e.getValue()).asObject().getValue().entrySet().stream()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
throw new SignatureDesyncException("flatMap");
|
||||
}
|
||||
|
||||
private static final DType fold = callable(generic(1), arg("list", list(generic(0))), arg("identity", generic(1)), arg("accumulator", callable(generic(1), arg("previous", generic(1)), arg("current", generic(0)))));
|
||||
public static Dynamic fold(DList args) {
|
||||
if (args.size() != 3) throw new IllegalArgumentException("Invalid number of arguments for fold: expected 3 but got " + args.size());
|
||||
if (args.size() != 3) throw new SignatureDesyncException("fold");
|
||||
return args.get(0).asList().getValue().stream().<Dynamic>map(Function.identity()).reduce(args.get(1), args.get(2).asCallable()::call);
|
||||
}
|
||||
|
||||
private static final DType forEach = callable(generic(1), arg("list", list(generic(0))), arg("operation", callable(generic(1), arg("item", generic(0)))));
|
||||
public static Dynamic forEach(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for forEach: expected 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new SignatureDesyncException("forEach");
|
||||
Dynamic result = new DNull();
|
||||
DCallable dc = args.get(1).asCallable();
|
||||
for (Dynamic dynamic : args.get(0).asList().getValue()) {
|
||||
|
@ -224,8 +297,9 @@ public class StandardLib {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static final DType toObject = callable(object(generic(1)), arg("list", list(generic(0))), arg("keyMapper", callable(STRING, arg("item", generic(0)))), arg("valueMapper", callable(generic(1), arg("item", generic(0)))));
|
||||
public static DObject toObject(DList args) {
|
||||
if (args.size() != 3) throw new IllegalArgumentException("Invalid number of arguments for args: expected 3 but got " + args.size());
|
||||
if (args.size() != 3) throw new SignatureDesyncException("toObject");
|
||||
DCallable keyMapper = args.get(1).asCallable();
|
||||
DCallable valueMapper = args.get(2).asCallable();
|
||||
return of(args.get(0)
|
||||
|
@ -241,29 +315,36 @@ public class StandardLib {
|
|||
}
|
||||
|
||||
// Objects
|
||||
private static final DType callableObject = callable(object(generic(0)).and(callable(null, (DType) null)), arg("object", object(generic(0))), arg("callable", callable(null, (DType) null)));
|
||||
public static DCallableObject callableObject(DList args) {
|
||||
if (args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for callableObject: expected 2 but got " + args.size());
|
||||
if (args.size() != 2) throw new SignatureDesyncException("callableObject");
|
||||
return new DCallableObject(args.get(0).asObject().getValue(), args.get(1).asCallable());
|
||||
}
|
||||
|
||||
private static final DType enumRepr = object(generic(0)).and(list(generic(0))).and(STRING);
|
||||
private static final DType enum_ = callable(enumRepr, arg("content", object(generic(0))))
|
||||
.and(callable(enumRepr.and(NUMBER), arg("content", object(generic(0))), arg("selectedKey", STRING)));
|
||||
public static DEnum enum_(DList args) {
|
||||
if (args.size() == 1) return new DEnum(args.get(0).asObject().getValue());
|
||||
else if (args.size() == 2) return new DEnum(args.get(0).asObject().getValue(), args.get(1).asString().getValue());
|
||||
else throw new IllegalArgumentException("Invalid number of arguments for enum: expected 1 or 2 but got " + args.size());
|
||||
else throw new SignatureDesyncException("enum");
|
||||
}
|
||||
|
||||
private static final DType keys = callable(list(STRING), arg("object", object(null)));
|
||||
public static DList keys(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for keys: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("keys");
|
||||
return of(args.get(0).asObject().getValue().keySet().stream().map(DFinal::of).toList());
|
||||
}
|
||||
|
||||
private static final DType values = callable(list(generic(0)), arg("object", object(generic(0))));
|
||||
public static DList values(DList args) {
|
||||
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for values: expected 1 but got " + args.size());
|
||||
if (args.size() != 1) throw new SignatureDesyncException("values");
|
||||
return of(args.get(0).asObject().getValue().values().stream().toList());
|
||||
}
|
||||
|
||||
private static final DType try_ = callable(object(null), arg("block", callable(null, arg("args", null, true))), arg("args", null, true));
|
||||
public static DObject try_(DList args) {
|
||||
if (args.isEmpty()) throw new IllegalArgumentException("Invalid number of arguments for try: expected 1 or more but got " + args.size());
|
||||
if (args.isEmpty()) throw new SignatureDesyncException("try");
|
||||
var callable = args.get(0).asCallable();
|
||||
var l = args.getValue();
|
||||
var innerArgs = of(l.subList(1, l.size()));
|
||||
|
@ -277,7 +358,7 @@ public class StandardLib {
|
|||
return of(Map.of(
|
||||
"result", result,
|
||||
"catch", of("catch", param -> {
|
||||
if (param.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for catch: expected 1 but got " + param.size());
|
||||
if (param.size() != 1) throw new SignatureDesyncException("catch");
|
||||
param.get(0).asCallable();
|
||||
return of(Map.of("result", result));
|
||||
}, serializedCatch)
|
||||
|
@ -286,7 +367,7 @@ public class StandardLib {
|
|||
return of(Map.of(
|
||||
"result", new DNull(),
|
||||
"catch", of("catch", param -> {
|
||||
if (param.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for catch: expected 1 but got " + param.size());
|
||||
if (param.size() != 1) throw new SignatureDesyncException("catch");
|
||||
var result = param.get(0).asCallable().call(of(Map.of(
|
||||
"message", of(le.getMessage())
|
||||
)));
|
||||
|
@ -296,8 +377,36 @@ public class StandardLib {
|
|||
}
|
||||
}
|
||||
|
||||
private static final DType fail = callable(null, arg("message", STRING))
|
||||
.and(callable(null));
|
||||
public static DNull fail(DList args) {
|
||||
if (args.size() > 1) throw new IllegalArgumentException("Invalid number of arguments for fail: expected 0 or 1 but got " + args.size());
|
||||
throw new RuntimeException(args.size() == 0 ? "Failed" : args.get(0).asString().getValue());
|
||||
if (args.size() > 1) throw new SignatureDesyncException("fail");
|
||||
throw new RuntimeException(args.isEmpty() ? "Failed" : args.get(0).asString().getValue());
|
||||
}
|
||||
|
||||
// Util
|
||||
public static DObject objectRepresentation(Map.Entry<String, ? extends Dynamic> entry) {
|
||||
return objectRepresentation(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
public static DObject objectRepresentation(String key, Dynamic value) {
|
||||
return of(Map.of("key", of(key), "value", value));
|
||||
}
|
||||
|
||||
record Entry(String key, Dynamic value) implements Map.Entry<String, Dynamic> {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dynamic setValue(Dynamic value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,4 +11,5 @@ module io.gitlab.jfronny.commons.muscript {
|
|||
exports io.gitlab.jfronny.muscript.data;
|
||||
exports io.gitlab.jfronny.muscript.data.dynamic;
|
||||
exports io.gitlab.jfronny.muscript.data.dynamic.additional;
|
||||
exports io.gitlab.jfronny.muscript.data.dynamic.type;
|
||||
}
|
|
@ -10,8 +10,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||
class ExceptionHandlingTest {
|
||||
@Test
|
||||
void catchStdlib() {
|
||||
assertEquals("Invalid number of arguments for isEmpty: expected 1 but got 0", assertThrows(LocationalException.class, () -> string("isEmpty()")).getMessage());
|
||||
assertEquals("Invalid number of arguments for isEmpty: expected 1 but got 0", string("try({->isEmpty()}).catch({e->e.message}).result"));
|
||||
assertEquals("Signature mismatch for isEmpty: expected <(collection: string | [any] | {any}) -> bool> but got <() -> any>", assertThrows(LocationalException.class, () -> string("isEmpty()")).getMessage());
|
||||
assertEquals("Signature mismatch for isEmpty: expected <(collection: string | [any] | {any}) -> bool> but got <() -> any>", string("try({->isEmpty()}).catch({e->e.message}).result"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -27,7 +27,7 @@ class ExceptionHandlingTest {
|
|||
|
||||
@Test
|
||||
void catchInner() {
|
||||
assertEquals("Got Invalid number of arguments for isEmpty: expected 1 but got 0", assertThrows(LocationalException.class, () -> parseScript("""
|
||||
assertEquals("Got Signature mismatch for isEmpty: expected <(collection: string | [any] | {any}) -> bool> but got <() -> any>", assertThrows(LocationalException.class, () -> parseScript("""
|
||||
inner = {-> isEmpty()}
|
||||
outer = {-> inner()}
|
||||
outer2 = {-> try({a->a()}, outer).catch({e -> fail('Got ' || e.message)}).result}
|
||||
|
|
Loading…
Reference in New Issue