java-commons/muscript/src/main/java/io/gitlab/jfronny/muscript/StandardLib.java
JFronny e84f8adc88
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
docs: Document StandardLib and GsonLib functions for μScript users
2023-08-25 18:53:04 +02:00

298 lines
14 KiB
Java

package io.gitlab.jfronny.muscript;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.ast.dynamic.Call;
import io.gitlab.jfronny.muscript.ast.dynamic.Variable;
import io.gitlab.jfronny.muscript.compiler.CodeLocation;
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.error.LocationalException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal.of;
public class StandardLib {
private static final Random rnd = new Random();
public static Scope createScope() {
return addTo(new Scope());
}
public static Scope addTo(Scope scope) {
return scope
.set("PI", Math.PI)
.set("E", Math.E)
.set("date", new DCallableObject(Map.of(
"today", new DDate(LocalDate::now)
), DFinal.of(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");
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 DDate(() -> LocalDate.of(a0, a1, a2));
}, "date")))
.set("time", new DCallableObject(Map.of(
"now", new DTime(LocalTime::now)
), DFinal.of(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");
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("toUpper", StandardLib::toUpper)
.set("toLower", StandardLib::toLower)
.set("contains", StandardLib::contains)
.set("replace", StandardLib::replace)
.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("callableObject", StandardLib::callableObject)
.set("enum", StandardLib::enum_)
.set("keys", StandardLib::keys)
.set("values", StandardLib::values)
.set("fail", StandardLib::fail)
.set("try", StandardLib::try_);
}
// 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 DNumber floor(DList args) {
if (args.size() != 1) throw new IllegalArgumentException("Invalid number of arguments for floor: expected 1 but got " + args.size());
return of(Math.floor(args.get(0).asNumber().getValue()));
}
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());
return of(Math.ceil(args.get(0).asNumber().getValue()));
}
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());
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());
}
// Strings
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());
return of(args.get(0).asString().getValue().toUpperCase());
}
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());
return of(args.get(0).asString().getValue().toLowerCase());
}
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());
Dynamic arg0 = args.get(0);
Dynamic arg1 = args.get(1);
boolean contained = false;
contained |= arg0.isList() && arg0.asList().getValue().contains(arg1);
contained |= arg0.isObject() && arg0.asObject().getValue().containsKey(arg1.asString().getValue());
contained |= arg0.isString() && arg0.asString().getValue().contains(arg1.asString().getValue());
return of(contained);
}
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());
return of(args.get(0).asString().getValue().replace(args.get(1).asString().getValue(), args.get(2).asString().getValue()));
}
// Lists
public static DList listOf(DList args) {
return args;
}
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());
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());
}
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());
Dynamic arg0 = args.get(0);
if (arg0.isObject()) return of(arg0.asObject().getValue().isEmpty());
return of(arg0.asList().isEmpty());
}
public static DList concat(DList args) {
return of(args.getValue().stream().flatMap(s -> s.asList().getValue().stream()).toList());
}
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());
DCallable dc = args.get(1).asCallable();
return of(args.get(0).asList().getValue().stream().filter(a -> dc.call(a).asBool().getValue()).toList());
}
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());
DCallable dc = args.get(1).asCallable();
return of(args.get(0).asList().getValue().stream().allMatch(a -> dc.call(a).asBool().getValue()));
}
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());
DCallable dc = args.get(1).asCallable();
return of(args.get(0).asList().getValue().stream().anyMatch(a -> dc.call(a).asBool().getValue()));
}
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());
DCallable dc = args.get(1).asCallable();
return of(args.get(0).asList().getValue().stream().flatMap(a -> dc.call(a).asList().getValue().stream()).toList());
}
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());
return args.get(0).asList().getValue().stream().<Dynamic>map(Function.identity()).reduce(args.get(1), args.get(2).asCallable()::call);
}
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());
Dynamic result = new DNull();
DCallable dc = args.get(1).asCallable();
for (Dynamic dynamic : args.get(0).asList().getValue()) {
result = dc.call(dynamic);
}
return result;
}
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());
DCallable keyMapper = args.get(1).asCallable();
DCallable valueMapper = args.get(2).asCallable();
return of(args.get(0)
.asList()
.getValue()
.stream()
.collect(Collectors.<Dynamic, String, Dynamic, LinkedHashMap<String, Dynamic>>toMap(
a -> keyMapper.call(a).asString().getValue(),
valueMapper::call,
(a, b) -> b,
LinkedHashMap::new
)));
}
// Objects
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());
return new DCallableObject(args.get(0).asObject().getValue(), args.get(1).asCallable());
}
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());
}
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());
return of(args.get(0).asObject().getValue().keySet().stream().map(DFinal::of).toList());
}
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());
return of(args.get(0).asObject().getValue().values().stream().toList());
}
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());
var callable = args.get(0).asCallable();
var l = args.getValue();
var innerArgs = of(l.subList(1, l.size()));
Supplier<Expr<?>> serializedCatch = () ->
new Call(CodeLocation.NONE,
new Variable(CodeLocation.NONE, "call"),
args.getValue().stream().map(a -> new Call.Arg(a.toExpr().asDynamicExpr(), false)).toList()
);
try {
var result = callable.call(innerArgs);
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());
param.get(0).asCallable();
return of(Map.of("result", result));
}, serializedCatch)
));
} catch (LocationalException le) {
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());
var result = param.get(0).asCallable().call(of(Map.of(
"message", of(le.getMessage())
)));
return of(Map.of("result", result));
}, serializedCatch)
));
}
}
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());
}
}