fixup query
This commit is contained in:
parent
dc5791815e
commit
bdb505fce7
@ -2,6 +2,7 @@ package eu.jonahbauer.json.query;
|
||||
|
||||
import eu.jonahbauer.json.*;
|
||||
import eu.jonahbauer.json.exceptions.JsonParserException;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import eu.jonahbauer.json.query.parser.ast.*;
|
||||
import eu.jonahbauer.json.query.util.Util;
|
||||
import lombok.experimental.UtilityClass;
|
||||
@ -9,11 +10,11 @@ import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.function.DoubleBinaryOperator;
|
||||
import java.util.function.DoubleUnaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.*;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -236,20 +237,20 @@ public class JsonMath {
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="error handling" defaultstate="collapsed">
|
||||
public static @NotNull Stream<@Nullable JsonValue> error(@Nullable JsonValue value) {
|
||||
public static @NotNull Generator<@Nullable JsonValue> error(@Nullable JsonValue value) {
|
||||
if (value != null) throw new JsonQueryUserException(value);
|
||||
return Stream.empty();
|
||||
return Generator.empty();
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> halt() {
|
||||
public static @NotNull Generator<@Nullable JsonValue> halt() {
|
||||
throw new JsonQueryHaltException(null, 0);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> halt(@Nullable JsonValue value) {
|
||||
public static @NotNull Generator<@Nullable JsonValue> halt(@Nullable JsonValue value) {
|
||||
throw new JsonQueryHaltException(value, 5);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> halt(@Nullable JsonValue value, @Nullable JsonValue code) {
|
||||
public static @NotNull Generator<@Nullable JsonValue> halt(@Nullable JsonValue value, @Nullable JsonValue code) {
|
||||
throw new JsonQueryHaltException(value, switch (code) {
|
||||
case JsonNumber(var number) -> (int) number;
|
||||
case null, default -> throw new JsonQueryException(STR."\{Util.type(value)} (\{Util.value(value)}) halt_error/1: number required");
|
||||
@ -258,11 +259,11 @@ public class JsonMath {
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="stream operations" defaultstate="collapsed">
|
||||
public static @NotNull Stream<@NotNull JsonValue> range(@Nullable JsonValue upto) {
|
||||
public static @NotNull Generator<@NotNull JsonValue> range(@Nullable JsonValue upto) {
|
||||
return range(new JsonNumber(0), upto);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonValue> range(@Nullable JsonValue from, @Nullable JsonValue upto) {
|
||||
public static @NotNull Generator<@NotNull JsonValue> range(@Nullable JsonValue from, @Nullable JsonValue upto) {
|
||||
var min = switch (from) {
|
||||
case JsonNumber(var number) -> number;
|
||||
case null, default -> throw new JsonQueryException("Range bounds must be numeric.");
|
||||
@ -271,81 +272,79 @@ public class JsonMath {
|
||||
case JsonNumber(var number) -> number;
|
||||
case null, default -> throw new JsonQueryException("Range bounds must be numeric.");
|
||||
};
|
||||
return IntStream.range((int) Math.ceil(min), (int) Math.ceil(max)).mapToObj(JsonNumber::new);
|
||||
return Generator.from(IntStream.range((int) Math.ceil(min), (int) Math.ceil(max)).boxed()).map(JsonNumber::new);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> range(@Nullable JsonValue from, @Nullable JsonValue upto, @Nullable JsonValue by) {
|
||||
public static @NotNull Generator<@Nullable JsonValue> range(@Nullable JsonValue from, @Nullable JsonValue upto, @Nullable JsonValue by) {
|
||||
if (compare(by, JsonNumber.ZERO) > 0) {
|
||||
return Stream.iterate(from, i -> compare(i, upto) < 0, i -> add(i, by));
|
||||
return Generator.from(Stream.iterate(from, i -> compare(i, upto) < 0, i -> add(i, by)));
|
||||
} else if (compare(by, JsonNumber.ZERO) < 0) {
|
||||
return Stream.iterate(from, i -> compare(i, upto) > 0, i -> add(i, by));
|
||||
return Generator.from(Stream.iterate(from, i -> compare(i, upto) > 0, i -> add(i, by)));
|
||||
} else {
|
||||
return Stream.empty();
|
||||
return Generator.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> limit(@NotNull Stream<@Nullable JsonValue> stream, @Nullable JsonValue limit) {
|
||||
public static @NotNull Generator<@Nullable JsonValue> limit(@NotNull Generator<@Nullable JsonValue> stream, @Nullable JsonValue limit) {
|
||||
if (compare(limit, JsonNumber.ZERO) > 0) {
|
||||
class State {
|
||||
@Nullable JsonValue limit;
|
||||
public State(@Nullable JsonValue limit) { this.limit = limit; }
|
||||
}
|
||||
return new Generator<>() {
|
||||
private @Nullable JsonValue i = limit;
|
||||
|
||||
return stream.gather(Gatherer.ofSequential(
|
||||
() -> new State(limit),
|
||||
(state, value, downstream) -> {
|
||||
state.limit = JsonMath.sub(state.limit, JsonNumber.ONE);
|
||||
downstream.push(value);
|
||||
return compare(state.limit, JsonNumber.ZERO) > 0;
|
||||
}
|
||||
));
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return compare(i, JsonNumber.ZERO) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonValue next() {
|
||||
if (compare(i, JsonNumber.ZERO) <= 0) throw new NoSuchElementException();
|
||||
i = sub(i, JsonNumber.ONE);
|
||||
return stream.next();
|
||||
}
|
||||
};
|
||||
} else if (Objects.equals(limit, JsonNumber.ZERO)) {
|
||||
return Stream.empty();
|
||||
return Generator.empty();
|
||||
} else {
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> first(@NotNull Stream<@Nullable JsonValue> stream) {
|
||||
public static @NotNull Generator<@Nullable JsonValue> first(@NotNull Generator<@Nullable JsonValue> stream) {
|
||||
return stream.limit(1);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> last(@NotNull Stream<@Nullable JsonValue> stream) {
|
||||
class State {
|
||||
boolean hasValue;
|
||||
@Nullable JsonValue value;
|
||||
}
|
||||
return stream.gather(Gatherer.ofSequential(
|
||||
State::new,
|
||||
Gatherer.Integrator.ofGreedy((state, value, _) -> {
|
||||
state.hasValue = true;
|
||||
state.value = value;
|
||||
return true;
|
||||
}),
|
||||
(state, downstream) -> {
|
||||
if (state.hasValue) downstream.push(state.value);
|
||||
public static @NotNull Generator<@Nullable JsonValue> last(@NotNull Generator<@Nullable JsonValue> stream) {
|
||||
return new Generator<>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return stream.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonValue next() throws NoSuchElementException {
|
||||
var out = stream.next();
|
||||
while (stream.hasNext()) {
|
||||
out = stream.next();
|
||||
}
|
||||
));
|
||||
return out;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> nth(@NotNull Stream<@Nullable JsonValue> stream, @Nullable JsonValue limit) {
|
||||
public static @NotNull Generator<@Nullable JsonValue> nth(@NotNull Generator<@Nullable JsonValue> stream, @Nullable JsonValue limit) {
|
||||
if (compare(limit, JsonNumber.ZERO) < 0) throw new JsonQueryException("nth doesn't support negative indices");
|
||||
class State {
|
||||
@Nullable JsonValue limit;
|
||||
public State(@Nullable JsonValue limit) { this.limit = limit; }
|
||||
}
|
||||
return stream.gather(Gatherer.ofSequential(
|
||||
() -> new State(sub(add(limit, JsonNumber.ONE), JsonNumber.ONE)),
|
||||
(state, value, downstream) -> {
|
||||
if (compare(state.limit, JsonNumber.ZERO) <= 0) {
|
||||
downstream.push(value);
|
||||
return false;
|
||||
} else {
|
||||
state.limit = sub(state.limit, JsonNumber.ONE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
));
|
||||
return Generator.of(() -> {
|
||||
for (var i = sub(add(limit, JsonNumber.ONE), JsonNumber.ONE); compare(i, JsonNumber.ZERO) > 0; i = sub(i, JsonNumber.ONE)) {
|
||||
stream.next();
|
||||
}
|
||||
return stream.next();
|
||||
});
|
||||
}
|
||||
|
||||
public static @NotNull Generator<@Nullable JsonValue> select(@Nullable JsonValue value, @NotNull JQFilter filter) {
|
||||
return filter.apply(value).mapMulti((result, consumer) -> {
|
||||
if (isTruthy(result)) consumer.accept(value);
|
||||
});
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
@ -430,16 +429,24 @@ public class JsonMath {
|
||||
return has(value, index);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> values(@Nullable JsonValue value) {
|
||||
public static @Nullable JsonValue first(@Nullable JsonValue value) {
|
||||
return index(value, JsonNumber.ZERO);
|
||||
}
|
||||
|
||||
public static @Nullable JsonValue last(@Nullable JsonValue value) {
|
||||
return index(value, new JsonNumber(-1));
|
||||
}
|
||||
|
||||
public static @NotNull Generator<@Nullable JsonValue> values(@Nullable JsonValue value) {
|
||||
return values(value, false);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@Nullable JsonValue> values(@Nullable JsonValue value, boolean optional) {
|
||||
public static @NotNull Generator<@Nullable JsonValue> values(@Nullable JsonValue value, boolean optional) {
|
||||
return switch (value) {
|
||||
case JsonArray array -> array.stream();
|
||||
case JsonObject object -> object.values().stream();
|
||||
case JsonArray array -> Generator.from(array);
|
||||
case JsonObject object -> Generator.from(object.values());
|
||||
case null, default -> {
|
||||
if (optional) yield Stream.empty();
|
||||
if (optional) yield Generator.empty();
|
||||
throw new JsonQueryException(STR."Cannot iterate over \{Util.type(value)} (\{Util.value(value)}).");
|
||||
}
|
||||
};
|
||||
@ -451,7 +458,7 @@ public class JsonMath {
|
||||
|
||||
public static @NotNull JsonValue mapValues(@Nullable JsonValue value, @NotNull JQFilter expression) {
|
||||
return switch (value) {
|
||||
case JsonArray array -> new JsonArray(array.stream().map(expression).flatMap(s -> s.limit(1)).toList());
|
||||
case JsonArray array -> new JsonArray(Generator.from(array).map(expression).flatMap(s -> s.limit(1)).toList());
|
||||
case JsonObject object -> {
|
||||
var out = new LinkedHashMap<String, JsonValue>();
|
||||
object.forEach((key, v) -> expression.apply(v).limit(1).forEach(newValue -> out.put(key, newValue)));
|
||||
@ -469,7 +476,7 @@ public class JsonMath {
|
||||
return JsonBoolean.valueOf(values(value).flatMap(expression).anyMatch(JsonMath::isTruthy));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean any(@NotNull Stream<@Nullable JsonValue> values, @NotNull JQFilter expression) {
|
||||
public static @NotNull JsonBoolean any(@NotNull Generator<@Nullable JsonValue> values, @NotNull JQFilter expression) {
|
||||
return JsonBoolean.valueOf(values.flatMap(expression).anyMatch(JsonMath::isTruthy));
|
||||
}
|
||||
|
||||
@ -481,7 +488,7 @@ public class JsonMath {
|
||||
return JsonBoolean.valueOf(values(value).flatMap(expression).allMatch(JsonMath::isTruthy));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean all(@NotNull Stream<@Nullable JsonValue> values, @NotNull JQFilter expression) {
|
||||
public static @NotNull JsonBoolean all(@NotNull Generator<@Nullable JsonValue> values, @NotNull JQFilter expression) {
|
||||
return JsonBoolean.valueOf(values.flatMap(expression).allMatch(JsonMath::isTruthy));
|
||||
}
|
||||
|
||||
@ -497,18 +504,18 @@ public class JsonMath {
|
||||
};
|
||||
}
|
||||
|
||||
private static @NotNull Stream<@Nullable JsonValue> flatten0(@Nullable JsonValue value) {
|
||||
private static @NotNull Generator<@Nullable JsonValue> flatten0(@Nullable JsonValue value) {
|
||||
return flatten0(value, -1);
|
||||
}
|
||||
|
||||
private static @NotNull Stream<@Nullable JsonValue> flatten0(@Nullable JsonValue value, int depth) {
|
||||
private static @NotNull Generator<@Nullable JsonValue> flatten0(@Nullable JsonValue value, int depth) {
|
||||
return switch (value) {
|
||||
case JsonArray array when depth == 0 -> Stream.of(array);
|
||||
case JsonArray array when depth == 0 -> Generator.of(array);
|
||||
case JsonArray array -> {
|
||||
var d = depth < 0 ? depth : depth - 1;
|
||||
yield array.stream().flatMap(v -> flatten0(v, d));
|
||||
yield Generator.from(array).flatMap(v -> flatten0(v, d));
|
||||
}
|
||||
case null, default -> Stream.of(value);
|
||||
case null, default -> Generator.of(value);
|
||||
};
|
||||
}
|
||||
|
||||
@ -703,8 +710,8 @@ public class JsonMath {
|
||||
return contains(container, content);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonArray> combinations(@Nullable JsonValue value) {
|
||||
if (length0(value) == 0) return Stream.of(JsonArray.EMPTY);
|
||||
public static @NotNull Generator<@NotNull JsonArray> combinations(@Nullable JsonValue value) {
|
||||
if (length0(value) == 0) return Generator.of(JsonArray.EMPTY);
|
||||
if (!(value instanceof JsonArray array)) throw indexError(value, JsonNumber.ZERO);
|
||||
|
||||
return values(array.getFirst())
|
||||
@ -716,7 +723,7 @@ public class JsonMath {
|
||||
}));
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonArray> combinations(@Nullable JsonValue value, @Nullable JsonValue n) {
|
||||
public static @NotNull Generator<@NotNull JsonArray> combinations(@Nullable JsonValue value, @Nullable JsonValue n) {
|
||||
if (!(n instanceof JsonNumber(var number))) throw new JsonQueryException("Range bounds must be numeric.");
|
||||
return combinations(new JsonArray(Collections.nCopies((int) Math.ceil(number), value)));
|
||||
}
|
||||
@ -747,6 +754,40 @@ public class JsonMath {
|
||||
return new JsonArray(out.stream().map(JsonArray::new).toList());
|
||||
}
|
||||
|
||||
private static final JsonString KEY = new JsonString("key");
|
||||
private static final JsonString VALUE = new JsonString("value");
|
||||
|
||||
public static @NotNull JsonArray toEntries(@Nullable JsonValue value) {
|
||||
return new JsonArray((switch (value) {
|
||||
case JsonArray array -> IntStream.range(0, array.size())
|
||||
.mapToObj(i -> JsonObject.of(
|
||||
"key", new JsonNumber(i),
|
||||
"value", array.get(i)
|
||||
));
|
||||
case JsonObject object -> object.entrySet().stream()
|
||||
.map(entry -> JsonObject.of(
|
||||
"key", new JsonString(entry.getKey()),
|
||||
"value", entry.getValue()
|
||||
));
|
||||
case null, default -> throw error(value, "has no keys");
|
||||
}).toList());
|
||||
}
|
||||
|
||||
public static @NotNull JsonObject fromEntries(@Nullable JsonValue value) {
|
||||
var out = new LinkedHashMap<String, JsonValue>();
|
||||
values(value).forEach(entry -> {
|
||||
var key = index(entry, KEY);
|
||||
var val = index(entry, VALUE);
|
||||
if (!(key instanceof JsonString(var k))) throw new JsonQueryException(STR."Cannot use \{Util.type(key)} (\{Util.value(key)}) as object key.");
|
||||
out.put(k, val);
|
||||
});
|
||||
return new JsonObject(out);
|
||||
}
|
||||
|
||||
public static @NotNull JsonObject withEntries(@Nullable JsonValue value, @NotNull JQFilter expression) {
|
||||
return fromEntries(map(toEntries(value), expression));
|
||||
}
|
||||
|
||||
private record SortEntry(@Nullable JsonValue value, @NotNull Iterable<@Nullable JsonValue> key) {
|
||||
private static final Comparator<Iterable<JsonValue>> LEXICOGRAPHIC_COMPARATOR = (a, b) -> compareLexicographically(a, b, JsonMath.COMPARATOR);
|
||||
private static final Comparator<SortEntry> COMPARATOR = Comparator.comparing(SortEntry::key, LEXICOGRAPHIC_COMPARATOR);
|
||||
@ -754,60 +795,116 @@ public class JsonMath {
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="filters" defaultstate="collapsed">
|
||||
public static boolean isArray(@Nullable JsonValue value) {
|
||||
public static @NotNull JsonBoolean isArray(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isArray0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isObject(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isObject0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isIterable(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isIterable0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isBoolean(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isBoolean0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isNumber(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isNumber0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isNormal(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isNormal0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isFinite(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isFinite0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isInfinite(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isInfinite0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isNan(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isNan0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isString(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isString0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isNull(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isNull0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isValue(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isValue0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isScalar(@Nullable JsonValue value) {
|
||||
return JsonBoolean.valueOf(isScalar0(value));
|
||||
}
|
||||
|
||||
public static @NotNull JsonBoolean isEmpty(@NotNull Generator<?> stream) {
|
||||
return JsonBoolean.valueOf(isEmpty0(stream));
|
||||
}
|
||||
|
||||
public static boolean isArray0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonArray;
|
||||
}
|
||||
|
||||
public static boolean isObject(@Nullable JsonValue value) {
|
||||
public static boolean isObject0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonObject;
|
||||
}
|
||||
|
||||
public static boolean isIterable(@Nullable JsonValue value) {
|
||||
return isArray(value) || isObject(value);
|
||||
public static boolean isIterable0(@Nullable JsonValue value) {
|
||||
return isArray0(value) || isObject0(value);
|
||||
}
|
||||
|
||||
public static boolean isBoolean(@Nullable JsonValue value) {
|
||||
public static boolean isBoolean0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonBoolean;
|
||||
}
|
||||
|
||||
public static boolean isNumber(@Nullable JsonValue value) {
|
||||
public static boolean isNumber0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonNumber;
|
||||
}
|
||||
|
||||
public static boolean isNormal(@Nullable JsonValue value) {
|
||||
public static boolean isNormal0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonNumber(var number) && Math.abs(number) >= Double.MIN_NORMAL;
|
||||
}
|
||||
|
||||
public static boolean isFinite(@Nullable JsonValue value) {
|
||||
public static boolean isFinite0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonNumber(var number) && Double.isFinite(number);
|
||||
}
|
||||
|
||||
public static boolean isInfinite(@Nullable JsonValue value) {
|
||||
public static boolean isInfinite0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonNumber(var number) && Double.isInfinite(number);
|
||||
}
|
||||
|
||||
public static boolean isNan(@Nullable JsonValue value) {
|
||||
public static boolean isNan0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonNumber(var number) && Double.isNaN(number);
|
||||
}
|
||||
|
||||
public static boolean isString(@Nullable JsonValue value) {
|
||||
public static boolean isString0(@Nullable JsonValue value) {
|
||||
return value instanceof JsonString;
|
||||
}
|
||||
|
||||
public static boolean isNull(@Nullable JsonValue value) {
|
||||
public static boolean isNull0(@Nullable JsonValue value) {
|
||||
return value == null;
|
||||
}
|
||||
|
||||
public static boolean isValue(@Nullable JsonValue value) {
|
||||
public static boolean isValue0(@Nullable JsonValue value) {
|
||||
return value != null;
|
||||
}
|
||||
|
||||
public static boolean isScalar(@Nullable JsonValue value) {
|
||||
return !isIterable(value);
|
||||
public static boolean isScalar0(@Nullable JsonValue value) {
|
||||
return !isIterable0(value);
|
||||
}
|
||||
|
||||
public static boolean isEmpty(@NotNull Stream<?> stream) {
|
||||
return stream.map(_ -> Boolean.TRUE).findFirst().isEmpty();
|
||||
public static boolean isEmpty0(@NotNull Generator<?> stream) {
|
||||
return !stream.hasNext();
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
@ -861,7 +958,7 @@ public class JsonMath {
|
||||
case JsonString string -> string;
|
||||
case JsonNumber _, JsonBoolean _ -> tostring(value);
|
||||
case null, default -> value;
|
||||
}).iterator();
|
||||
});
|
||||
|
||||
if (!it.hasNext()) return JsonString.EMPTY;
|
||||
|
||||
@ -943,14 +1040,14 @@ public class JsonMath {
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="regex" defaultstate="collapsed">
|
||||
public static @NotNull Stream<@NotNull JsonValue> match(
|
||||
public static @NotNull Generator<@NotNull JsonValue> match(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args
|
||||
) {
|
||||
var input = regexInput(context);
|
||||
return match(context, args, false).flatMap(Function.identity()).map(result -> match0(input, result));
|
||||
}
|
||||
|
||||
private static @NotNull Stream<Stream<@NotNull MatchResult>> match(
|
||||
private static @NotNull Generator<Generator<@NotNull MatchResult>> match(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args, boolean global
|
||||
) {
|
||||
var input = regexInput(context);
|
||||
@ -969,7 +1066,7 @@ public class JsonMath {
|
||||
};
|
||||
}
|
||||
|
||||
private static @NotNull Stream<@NotNull MatchResult> match(
|
||||
private static @NotNull Generator<@NotNull MatchResult> match(
|
||||
@NotNull String input, @Nullable JsonValue pattern, @Nullable JsonValue flags, boolean global
|
||||
) {
|
||||
if (!(pattern instanceof JsonString(var patternString))) throw error(pattern, "is not a string");
|
||||
@ -1005,17 +1102,24 @@ public class JsonMath {
|
||||
return out;
|
||||
}
|
||||
|
||||
private static @NotNull Stream<@NotNull MatchResult> matchesAsStream(@NotNull Matcher matcher) {
|
||||
var it = new Iterator<MatchResult>() {
|
||||
@Override
|
||||
public boolean hasNext() { return matcher.find(); }
|
||||
private static @NotNull Generator<@NotNull MatchResult> matchesAsStream(@NotNull Matcher matcher) {
|
||||
return new Generator<>() {
|
||||
private boolean valid = false;
|
||||
|
||||
@Override
|
||||
public MatchResult next() { return matcher.toMatchResult(); }
|
||||
public boolean hasNext() {
|
||||
if (valid) return true;
|
||||
return valid = matcher.find();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchResult next() {
|
||||
if (!valid) valid = matcher.find();
|
||||
if (!valid) throw new NoSuchElementException();
|
||||
valid = false;
|
||||
return matcher.toMatchResult();
|
||||
}
|
||||
};
|
||||
|
||||
var split = Spliterators.spliteratorUnknownSize(it, Spliterator.ORDERED);
|
||||
return StreamSupport.stream(split, false);
|
||||
}
|
||||
|
||||
private static @NotNull JsonValue match0(@NotNull String input, @NotNull MatchResult result) {
|
||||
@ -1047,13 +1151,13 @@ public class JsonMath {
|
||||
throw error(context.root(), "cannot be matched, as it is not a string");
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonValue> test(
|
||||
public static @NotNull Generator<@NotNull JsonValue> test(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args
|
||||
) {
|
||||
return Util.lazy(() -> JsonBoolean.valueOf(!isEmpty(match(context, args, false))));
|
||||
return Generator.of(() -> JsonBoolean.valueOf(!isEmpty0(match(context, args, false))));
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonValue> capture(
|
||||
public static @NotNull Generator<@NotNull JsonValue> capture(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args
|
||||
) {
|
||||
return match(context, args, false).flatMap(Function.identity()).map(JsonMath::capture0);
|
||||
@ -1065,51 +1169,61 @@ public class JsonMath {
|
||||
return new JsonObject(out);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonValue> scan(
|
||||
public static @NotNull Generator<@NotNull JsonValue> scan(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args
|
||||
) {
|
||||
return match(context, args, true).flatMap(Function.identity()).map(MatchResult::group).map(JsonString::new);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonValue> split(
|
||||
public static @NotNull Generator<@NotNull JsonValue> split(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args
|
||||
) {
|
||||
return Util.lazy(() -> new JsonArray(splits(context, args).toList()));
|
||||
return Generator.of(() -> new JsonArray(splits(context, args).toList()));
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonValue> splits(
|
||||
public static @NotNull Generator<@NotNull JsonValue> splits(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args
|
||||
) {
|
||||
if (!(context.root() instanceof JsonString(var inputString))) throw error(context.root(), "cannot be matched, as it is not a string");
|
||||
return match(context, args, true).flatMap(s -> s.gather(splits0(inputString)));
|
||||
return match(context, args, true).flatMap(it -> new Generator<>() {
|
||||
private boolean finished;
|
||||
private int offset;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !finished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonValue next() throws NoSuchElementException {
|
||||
if (it.hasNext()) {
|
||||
var match = it.next();
|
||||
var out = new JsonString(inputString.substring(offset, match.start()));
|
||||
offset = match.end();
|
||||
return out;
|
||||
} else if (!finished) {
|
||||
finished = true;
|
||||
return new JsonString(inputString.substring(offset));
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static @NotNull Gatherer<MatchResult, ?, JsonValue> splits0(@NotNull String input) {
|
||||
class State { int offset; }
|
||||
return Gatherer.ofSequential(
|
||||
State::new,
|
||||
Gatherer.Integrator.ofGreedy((state, value, downstream) -> {
|
||||
downstream.push(new JsonString(input.substring(state.offset, value.start())));
|
||||
state.offset = value.end();
|
||||
return true;
|
||||
}),
|
||||
(state, downstream) -> downstream.push(new JsonString(input.substring(state.offset)))
|
||||
);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonValue> sub(
|
||||
public static @NotNull Generator<@NotNull JsonValue> sub(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args
|
||||
) {
|
||||
return sub0(context, args, false);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull JsonValue> gsub(
|
||||
public static @NotNull Generator<@NotNull JsonValue> gsub(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args
|
||||
) {
|
||||
return sub0(context, args, true);
|
||||
}
|
||||
|
||||
private static @NotNull Stream<@NotNull JsonValue> sub0(
|
||||
private static @NotNull Generator<@NotNull JsonValue> sub0(
|
||||
@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args, boolean global
|
||||
) {
|
||||
var input = regexInput(context);
|
||||
@ -1120,12 +1234,11 @@ public class JsonMath {
|
||||
};
|
||||
var subst = args.get(1);
|
||||
|
||||
return match(context, matchArgs, global).flatMap(s -> {
|
||||
return match(context, matchArgs, global).flatMap(it -> {
|
||||
var offset = 0;
|
||||
var fragments = new ArrayList<String>();
|
||||
var values = new ArrayList<JQExpression>();
|
||||
|
||||
var it = s.iterator();
|
||||
while (it.hasNext()) {
|
||||
var result = it.next();
|
||||
var capture = capture0(result);
|
||||
@ -1184,6 +1297,33 @@ public class JsonMath {
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="time" defaultstate="collapsed">
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
.withZone(ZoneId.of("UTC"));
|
||||
|
||||
public static @NotNull JsonNumber fromdateiso8601(@Nullable JsonValue value) {
|
||||
return JsonNumber.valueOf(switch (value) {
|
||||
case JsonString(var string) -> DATE_TIME_FORMATTER.parse(string, Instant::from).getEpochSecond();
|
||||
case null, default -> throw new JsonQueryException("fromdateiso8601/1 requires string inputs");
|
||||
});
|
||||
}
|
||||
|
||||
public static @NotNull JsonNumber fromdate(@Nullable JsonValue value) {
|
||||
return fromdateiso8601(value);
|
||||
}
|
||||
|
||||
public static @NotNull JsonString todateiso8601(@Nullable JsonValue value) {
|
||||
return new JsonString(switch (value) {
|
||||
case JsonNumber(var number) -> DATE_TIME_FORMATTER.format(Instant.ofEpochSecond((long) number));
|
||||
case null, default -> throw new JsonQueryException("todateiso8601/1 requires number inputs");
|
||||
});
|
||||
}
|
||||
|
||||
public static @NotNull JsonString todate(@Nullable JsonValue value) {
|
||||
return todateiso8601(value);
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="misc" defaultstate="collapsed">
|
||||
public static @NotNull JsonNumber length(@Nullable JsonValue value) {
|
||||
return new JsonNumber(length0(value));
|
||||
@ -1199,6 +1339,21 @@ public class JsonMath {
|
||||
case null -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
public static @NotNull Generator<@Nullable JsonValue> repeat(@NotNull Supplier<@NotNull Generator<@Nullable JsonValue>> delegate) {
|
||||
return new Generator<>() {
|
||||
private @NotNull Generator<JsonValue> current = Generator.empty();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() { return true; }
|
||||
|
||||
@Override
|
||||
public JsonValue next() throws NoSuchElementException {
|
||||
while (!current.hasNext()) current = delegate.get();
|
||||
return current.next();
|
||||
}
|
||||
};
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="math library" defaultstate="collapsed">
|
||||
|
@ -1,8 +1,9 @@
|
||||
package eu.jonahbauer.json.query;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import eu.jonahbauer.json.query.parser.JQParser;
|
||||
import eu.jonahbauer.json.query.parser.ast.JQExpression;
|
||||
import eu.jonahbauer.json.query.parser.ast.JQProgram;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -10,25 +11,23 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class JsonQuery {
|
||||
private final @NotNull JQExpression expression;
|
||||
private final @NotNull JQProgram programm;
|
||||
|
||||
public static @NotNull JsonQuery parse(@NotNull String query) {
|
||||
try {
|
||||
var parser = new JQParser(query);
|
||||
var programm = parser.parseTopLevel();
|
||||
if (programm.expression() == null) throw new IllegalArgumentException();
|
||||
return new JsonQuery(programm.expression());
|
||||
return new JsonQuery(programm);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public @NotNull Stream<@NotNull JsonValue> run(@Nullable JsonValue value) {
|
||||
var context = new JQExpression.Context(value);
|
||||
return expression.evaluate(context);
|
||||
public @NotNull Generator<@NotNull JsonValue> run(@Nullable JsonValue value) {
|
||||
return programm.run(value);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
|
||||
final class ConcatGenerator<T> implements Generator<T> {
|
||||
@ -14,15 +15,28 @@ final class ConcatGenerator<T> implements Generator<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() throws EndOfStreamException {
|
||||
public boolean hasNext() {
|
||||
while (!generators.isEmpty()) {
|
||||
try {
|
||||
var current = generators.peek();
|
||||
return current.next();
|
||||
} catch (EndOfStreamException ex) {
|
||||
var current = generators.peek();
|
||||
if (!current.hasNext()) {
|
||||
generators.remove();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw new EndOfStreamException();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
while (!generators.isEmpty()) {
|
||||
var current = generators.peek();
|
||||
if (!current.hasNext()) {
|
||||
generators.remove();
|
||||
} else {
|
||||
return current.next();
|
||||
}
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,17 @@
|
||||
package eu.jonahbauer.json.query.impl;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
enum EmptyGenerator implements Generator<Object> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Object next() throws EndOfStreamException {
|
||||
throw new EndOfStreamException();
|
||||
public boolean hasNext() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object next() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
@ -15,13 +15,18 @@ final class FlatMappingGenerator<T, S> implements Generator<S> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public S next() throws EndOfStreamException {
|
||||
while (true) {
|
||||
try {
|
||||
return current.next();
|
||||
} catch (EndOfStreamException _) {
|
||||
current = function.apply(source.next());
|
||||
}
|
||||
public boolean hasNext() {
|
||||
while (!current.hasNext() && source.hasNext()) {
|
||||
current = function.apply(source.next());
|
||||
}
|
||||
return current.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public S next() {
|
||||
while (!current.hasNext() && source.hasNext()) {
|
||||
current = function.apply(source.next());
|
||||
}
|
||||
return current.next();
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,56 @@ package eu.jonahbauer.json.query.impl;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface Generator<T> {
|
||||
T next() throws EndOfStreamException;
|
||||
boolean hasNext();
|
||||
T next() throws NoSuchElementException;
|
||||
|
||||
default void forEach(@NotNull Consumer<? super T> action) {
|
||||
while (hasNext()) {
|
||||
action.accept(next());
|
||||
}
|
||||
}
|
||||
|
||||
default T reduce(T seed, @NotNull BinaryOperator<T> operator) {
|
||||
while (hasNext()) {
|
||||
seed = operator.apply(seed, next());
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
default boolean anyMatch(@NotNull Predicate<? super T> predicate) {
|
||||
while (hasNext()) {
|
||||
if (predicate.test(next())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean allMatch(@NotNull Predicate<? super T> predicate) {
|
||||
while (hasNext()) {
|
||||
if (!predicate.test(next())) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default @NotNull List<T> toList() {
|
||||
var out = new ArrayList<T>();
|
||||
while (hasNext()) {
|
||||
out.add(next());
|
||||
}
|
||||
return Collections.unmodifiableList(out);
|
||||
}
|
||||
|
||||
default @NotNull Generator<T> filter(@NotNull Predicate<? super T> predicate) {
|
||||
return new FilterGenerator<>(this, predicate);
|
||||
}
|
||||
|
||||
default @NotNull Generator<T> limit(int count) {
|
||||
return new LimitGenerator<>(this, count);
|
||||
}
|
||||
|
||||
default <S> @NotNull Generator<S> map(@NotNull Function<? super T, ? extends S> function) {
|
||||
return new MappingGenerator<>(this, function);
|
||||
@ -16,6 +61,10 @@ public interface Generator<T> {
|
||||
return new FlatMappingGenerator<>(this, function);
|
||||
}
|
||||
|
||||
default <S> @NotNull Generator<S> mapMulti(@NotNull BiConsumer<? super T, ? super Consumer<S>> function) {
|
||||
return new MapMultiGenerator<>(this, function);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> @NotNull Generator<T> empty() {
|
||||
return (Generator<T>) EmptyGenerator.INSTANCE;
|
||||
@ -29,5 +78,19 @@ public interface Generator<T> {
|
||||
return new SingletonGenerator<>(value);
|
||||
}
|
||||
|
||||
class EndOfStreamException extends RuntimeException {}
|
||||
static <T> @NotNull Generator<T> of(@NotNull Supplier<T> value) {
|
||||
return new SupplierGenerator<>(value);
|
||||
}
|
||||
|
||||
static <T> @NotNull Generator<T> from(@NotNull Iterable<T> value) {
|
||||
return new IteratorGenerator<>(value.iterator());
|
||||
}
|
||||
|
||||
static <T> @NotNull Generator<T> from(@NotNull Iterator<T> value) {
|
||||
return new IteratorGenerator<>(value);
|
||||
}
|
||||
|
||||
static <T> @NotNull Generator<T> from(@NotNull Stream<T> value) {
|
||||
return new IteratorGenerator<>(value.iterator());
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,25 @@ package eu.jonahbauer.json.query.impl;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
record MappingGenerator<T, S>(@NotNull Generator<T> delegate, @NotNull Function<? super T, ? extends S> function) implements Generator<S> {
|
||||
final class MappingGenerator<T, S> implements Generator<S> {
|
||||
private final @NotNull Generator<T> delegate;
|
||||
private final @NotNull Function<? super T, ? extends S> function;
|
||||
|
||||
public MappingGenerator(@NotNull Generator<T> delegate, @NotNull Function<? super T, ? extends S> function) {
|
||||
this.delegate = Objects.requireNonNull(delegate);
|
||||
this.function = Objects.requireNonNull(function);
|
||||
}
|
||||
|
||||
@Override
|
||||
public S next() throws EndOfStreamException {
|
||||
public boolean hasNext() {
|
||||
return delegate.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public S next() {
|
||||
return function.apply(delegate.next());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package eu.jonahbauer.json.query.impl;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
final class SingletonGenerator<T> implements Generator<T> {
|
||||
private boolean done;
|
||||
private T value;
|
||||
@ -9,13 +11,17 @@ final class SingletonGenerator<T> implements Generator<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() throws EndOfStreamException {
|
||||
if (!done) {
|
||||
var out = value;
|
||||
done = true;
|
||||
value = null;
|
||||
return value;
|
||||
}
|
||||
throw new EndOfStreamException();
|
||||
public boolean hasNext() {
|
||||
return !done;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (done) throw new NoSuchElementException();
|
||||
|
||||
var out = value;
|
||||
done = true;
|
||||
value = null;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
@ -107,15 +107,11 @@ public class JQParser {
|
||||
return new JQFunction(name, params, body);
|
||||
}
|
||||
|
||||
private @NotNull JQExpression parseExpression() throws IOException {
|
||||
public @NotNull JQExpression parseExpression() throws IOException {
|
||||
if (peek(JQTokenKind.DEF)) {
|
||||
|
||||
} else if (peek(JQTokenKind.REDUCE)) {
|
||||
|
||||
} else if (peek(JQTokenKind.FOREACH)) {
|
||||
|
||||
} else if (peek(JQTokenKind.IF)) {
|
||||
|
||||
var function = parseFuncDef();
|
||||
var expr = parsePipeExpression();
|
||||
return new JQFunctionDefinition(function, expr);
|
||||
} else if (peek(JQTokenKind.LABEL)) {
|
||||
|
||||
} else {
|
||||
@ -254,7 +250,7 @@ public class JQParser {
|
||||
}
|
||||
|
||||
private @NotNull JQExpression parseErrorSuppression() throws IOException {
|
||||
var expression = parseTermOrAs();
|
||||
var expression = parseControlFlow();
|
||||
if (tryConsume(JQTokenKind.QUESTION_MARK)) {
|
||||
return new JQTryExpression(expression);
|
||||
} else {
|
||||
@ -262,35 +258,82 @@ public class JQParser {
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull JQExpression parseTermOrAs() throws IOException {
|
||||
var term = parseTerm();
|
||||
if (tryConsume(JQTokenKind.AS)) {
|
||||
var patterns = new ArrayList<JQAsExpression.Pattern>();
|
||||
patterns.add(parsePattern());
|
||||
while (tryConsume(JQTokenKind.ALTERNATION)) {
|
||||
patterns.add(parsePattern());
|
||||
private @NotNull JQExpression parseControlFlow() throws IOException {
|
||||
if (tryConsume(JQTokenKind.REDUCE)) {
|
||||
var expr = parseTerm();
|
||||
consume(JQTokenKind.AS);
|
||||
var patterns = parsePatterns();
|
||||
consume(JQTokenKind.LPAREN);
|
||||
var init = parseExpression();
|
||||
consume(JQTokenKind.SEMICOLON);
|
||||
var update = parseExpression();
|
||||
consume(JQTokenKind.RPAREN);
|
||||
return new JQReduceExpression(expr, patterns, init, update);
|
||||
} else if (tryConsume(JQTokenKind.FOREACH)) {
|
||||
var expr = parseTerm();
|
||||
consume(JQTokenKind.AS);
|
||||
var patterns = parsePatterns();
|
||||
consume(JQTokenKind.LPAREN);
|
||||
var init = parseExpression();
|
||||
consume(JQTokenKind.SEMICOLON);
|
||||
var update = parseExpression();
|
||||
var extract = tryConsume(JQTokenKind.SEMICOLON) ? parseExpression() : new JQRootExpression();
|
||||
consume(JQTokenKind.RPAREN);
|
||||
return new JQForEachExpression(expr, patterns, init, update, extract);
|
||||
} else if (tryConsume(JQTokenKind.IF)) {;
|
||||
var conds = new ArrayList<JQExpression>();
|
||||
var thens = new ArrayList<JQExpression>();
|
||||
|
||||
do {
|
||||
conds.add(parseExpression());
|
||||
consume(JQTokenKind.THEN);
|
||||
thens.add(parseExpression());
|
||||
} while (tryConsume(JQTokenKind.ELSE_IF));
|
||||
|
||||
var otherwise = tryConsume(JQTokenKind.ELSE) ? parseExpression() : null;
|
||||
consume(JQTokenKind.END);
|
||||
|
||||
var out = new JQIfExpression(conds.removeLast(), thens.removeLast(), otherwise);
|
||||
while (!conds.isEmpty()) {
|
||||
out = new JQIfExpression(conds.removeLast(), thens.removeLast(), out);
|
||||
}
|
||||
consume(JQTokenKind.PIPE);
|
||||
var expr = parseExpression();
|
||||
return new JQAsExpression(term, patterns, expr);
|
||||
|
||||
return out;
|
||||
} else {
|
||||
var term = parseTerm();
|
||||
if (tryConsume(JQTokenKind.AS)) {
|
||||
var patterns = parsePatterns();
|
||||
consume(JQTokenKind.PIPE);
|
||||
var expr = parseExpression();
|
||||
return new JQAsExpression(term, patterns, expr);
|
||||
}
|
||||
return term;
|
||||
}
|
||||
return term;
|
||||
}
|
||||
|
||||
private @NotNull JQAsExpression.Pattern parsePattern() throws IOException {
|
||||
private @NotNull JQPatterns parsePatterns() throws IOException {
|
||||
var patterns = new ArrayList<JQPatterns.Pattern>();
|
||||
patterns.add(parsePattern());
|
||||
while (tryConsume(JQTokenKind.ALTERNATION)) {
|
||||
patterns.add(parsePattern());
|
||||
}
|
||||
return new JQPatterns(patterns);
|
||||
}
|
||||
|
||||
private @NotNull JQPatterns.Pattern parsePattern() throws IOException {
|
||||
if (tryConsume(JQTokenKind.LBRACKET)) {
|
||||
var patterns = new ArrayList<JQAsExpression.Pattern>();
|
||||
var patterns = new ArrayList<JQPatterns.Pattern>();
|
||||
do {
|
||||
patterns.add(parsePattern());
|
||||
} while (tryConsume(JQTokenKind.COMMA));
|
||||
consume(JQTokenKind.RBRACKET);
|
||||
return new JQAsExpression.Pattern.ArrayPattern(patterns);
|
||||
return new JQPatterns.Pattern.ArrayPattern(patterns);
|
||||
} else if (tryConsume(JQTokenKind.LBRACE)) {
|
||||
var patterns = new LinkedHashMap<JQExpression, JQAsExpression.Pattern>();
|
||||
var patterns = new LinkedHashMap<JQExpression, JQPatterns.Pattern>();
|
||||
do {
|
||||
if (tryConsume(JQTokenKind.DOLLAR)) {
|
||||
var ident = consume(JQTokenKind.IDENT).text();
|
||||
var pattern = new JQAsExpression.Pattern.ValuePattern("$" + ident);
|
||||
var pattern = new JQPatterns.Pattern.ValuePattern("$" + ident);
|
||||
patterns.put(new JQConstant(JsonString.valueOf(ident)), pattern);
|
||||
} else if (peek(JQTokenKind.IDENT)) {
|
||||
var ident = consume(JQTokenKind.IDENT).text();
|
||||
@ -311,11 +354,11 @@ public class JQParser {
|
||||
}
|
||||
} while (tryConsume(JQTokenKind.COMMA));
|
||||
consume(JQTokenKind.RBRACE);
|
||||
return new JQAsExpression.Pattern.ObjectPattern(patterns);
|
||||
return new JQPatterns.Pattern.ObjectPattern(patterns);
|
||||
} else {
|
||||
consume(JQTokenKind.DOLLAR);
|
||||
var ident = consume(JQTokenKind.IDENT).text();
|
||||
return new JQAsExpression.Pattern.ValuePattern("$" + ident);
|
||||
return new JQPatterns.Pattern.ValuePattern("$" + ident);
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,7 +443,7 @@ public class JQParser {
|
||||
}
|
||||
|
||||
private @NotNull JQExpression parseVariableExpression() throws IOException {
|
||||
var dollar = consume(JQTokenKind.DOLLAR);
|
||||
consume(JQTokenKind.DOLLAR);
|
||||
var ident = consume(JQTokenKind.IDENT).text();
|
||||
return new JQVariableExpression("$" + ident);
|
||||
}
|
||||
|
@ -2,17 +2,12 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Gatherer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SuppressWarnings("preview")
|
||||
public record JQAlternativeExpression(@NotNull JQExpression first, @NotNull JQExpression second) implements JQExpression {
|
||||
@ -22,8 +17,48 @@ public record JQAlternativeExpression(@NotNull JQExpression first, @NotNull JQEx
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return first.evaluate(context).gather(new AlternativeGatherer(second, context));
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
var first = this.first.evaluate(context);
|
||||
var second = this.second.evaluate(context);
|
||||
return new Generator<>() {
|
||||
private boolean empty = true;
|
||||
|
||||
private JsonValue value;
|
||||
private boolean hasValue;
|
||||
|
||||
private boolean advance() {
|
||||
if (hasValue) return true;
|
||||
while (first.hasNext()) {
|
||||
var value = first.next();
|
||||
if (JsonMath.isTruthy(value)) {
|
||||
this.empty = false;
|
||||
this.value = value;
|
||||
this.hasValue = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (empty && second.hasNext()) {
|
||||
this.value = second.next();
|
||||
this.hasValue = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return advance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JsonValue next() throws NoSuchElementException {
|
||||
if (!advance()) throw new NoSuchElementException();
|
||||
var out = value;
|
||||
hasValue = false;
|
||||
value = null;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -35,46 +70,4 @@ public record JQAlternativeExpression(@NotNull JQExpression first, @NotNull JQEx
|
||||
public @NotNull String toString() {
|
||||
return first + " // " + second;
|
||||
}
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
private static class State {
|
||||
private boolean empty = true;
|
||||
}
|
||||
|
||||
private record AlternativeGatherer(
|
||||
@NotNull JQExpression expression,
|
||||
@NotNull Context context
|
||||
) implements Gatherer<JsonValue, State, JsonValue> {
|
||||
|
||||
@Override
|
||||
public @NotNull Supplier<State> initializer() {
|
||||
return State::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Integrator<State, JsonValue, JsonValue> integrator() {
|
||||
return Integrator.ofGreedy((state, element, downstream) -> {
|
||||
if (JsonMath.isTruthy(element)) {
|
||||
state.empty = false;
|
||||
return downstream.push(element);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BinaryOperator<State> combiner() {
|
||||
return (state1, state2) -> new State(state1.empty && state2.empty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BiConsumer<State, Downstream<? super JsonValue>> finisher() {
|
||||
return (state, downstream) -> {
|
||||
if (!state.empty) return;
|
||||
var it = expression.evaluate(context).iterator();
|
||||
while (it.hasNext() && downstream.push(it.next()));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,11 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonArray;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.util.Util;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQArrayConstructionExpression(@NotNull JQExpression expression) implements JQExpression {
|
||||
public JQArrayConstructionExpression {
|
||||
@ -15,8 +14,8 @@ public record JQArrayConstructionExpression(@NotNull JQExpression expression) im
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Util.lazy(() -> new JsonArray(expression.evaluate(context).toList()));
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Generator.of(() -> new JsonArray(expression.evaluate(context).toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,57 +1,26 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.*;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.JsonQueryException;
|
||||
import eu.jonahbauer.json.query.util.Util;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Objects;
|
||||
|
||||
public record JQAsExpression(
|
||||
@NotNull JQExpression variable, @NotNull List<@NotNull Pattern> patterns, @NotNull JQExpression expression
|
||||
@NotNull JQExpression variable, @NotNull JQPatterns patterns, @NotNull JQExpression expression
|
||||
) implements JQExpression {
|
||||
|
||||
public JQAsExpression {
|
||||
Objects.requireNonNull(variable);
|
||||
Objects.requireNonNull(expression);
|
||||
|
||||
patterns = List.copyOf(patterns);
|
||||
Objects.requireNonNull(patterns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
var variables = new HashMap<String, JsonValue>();
|
||||
patterns.stream().map(Pattern::variables).flatMap(Set::stream).forEach(key -> variables.put(key, null));
|
||||
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return variable.evaluate(context)
|
||||
.flatMap(value -> {
|
||||
Stream<Map<String, JsonValue>> result = null;
|
||||
|
||||
// find first pattern that does not throw
|
||||
var it = patterns.iterator();
|
||||
while (it.hasNext()) {
|
||||
try {
|
||||
result = it.next().bind(context, value);
|
||||
} catch (JsonQueryException ex) {
|
||||
if (!it.hasNext()) throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
// execute expression for all possible pattern matches
|
||||
assert result != null;
|
||||
return result
|
||||
.map(vars -> {
|
||||
var out = new HashMap<>(variables);
|
||||
out.putAll(vars);
|
||||
return context.withVariables(out);
|
||||
})
|
||||
.flatMap(expression::evaluate);
|
||||
});
|
||||
.flatMap(value -> patterns.bind(context, value).map(context::withVariables).flatMap(expression::evaluate));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,113 +30,7 @@ public record JQAsExpression(
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return variable
|
||||
+ " as " + patterns.stream().map(Objects::toString).collect(Collectors.joining(" ?// "))
|
||||
+ " | " + expression;
|
||||
return variable + " as " + patterns + " | " + expression;
|
||||
}
|
||||
|
||||
public sealed interface Pattern {
|
||||
@NotNull Set<@NotNull String> variables();
|
||||
@NotNull Stream<Map<@NotNull String, @Nullable JsonValue>> bind(@NotNull Context context, @Nullable JsonValue value);
|
||||
|
||||
record ValuePattern(@NotNull String name) implements Pattern {
|
||||
public ValuePattern {
|
||||
Objects.requireNonNull(name);
|
||||
if (!name.startsWith("$")) throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<@NotNull String> variables() {
|
||||
return Set.of(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<Map<@NotNull String, @Nullable JsonValue>> bind(@NotNull Context context, @Nullable JsonValue value) {
|
||||
var map = new HashMap<String, JsonValue>();
|
||||
map.put(name, value);
|
||||
return Stream.of(Collections.unmodifiableMap(map));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
record ArrayPattern(@NotNull List<@NotNull Pattern> patterns) implements Pattern {
|
||||
public ArrayPattern {
|
||||
patterns = List.copyOf(patterns);
|
||||
if (patterns.isEmpty()) throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<@NotNull String> variables() {
|
||||
var out = new HashSet<String>();
|
||||
patterns.forEach(p -> out.addAll(p.variables()));
|
||||
return Collections.unmodifiableSet(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<Map<@NotNull String, @Nullable JsonValue>> bind(@NotNull Context context, @Nullable JsonValue value) {
|
||||
var streams = new ArrayList<Supplier<Stream<Map<String, JsonValue>>>>();
|
||||
|
||||
for (int i = 0; i < patterns.size(); i++) {
|
||||
var k = new JsonNumber(i);
|
||||
var v = JsonMath.index(value, k);
|
||||
|
||||
var pattern = patterns.get(i);
|
||||
streams.add(() -> pattern.bind(context, v));
|
||||
}
|
||||
|
||||
return Util.crossReversed(streams).map(list -> {
|
||||
var map = new HashMap<String, JsonValue>();
|
||||
list.forEach(map::putAll);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return patterns.stream().map(Objects::toString).collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
}
|
||||
|
||||
record ObjectPattern(@NotNull SequencedMap<@NotNull JQExpression, @NotNull Pattern> patterns) implements Pattern {
|
||||
|
||||
public ObjectPattern {
|
||||
patterns = Collections.unmodifiableSequencedMap(new LinkedHashMap<>(patterns));
|
||||
if (patterns.isEmpty()) throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<@NotNull String> variables() {
|
||||
var out = new HashSet<String>();
|
||||
patterns.values().forEach(p -> out.addAll(p.variables()));
|
||||
return Collections.unmodifiableSet(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<Map<@NotNull String, @Nullable JsonValue>> bind(@NotNull Context context, @Nullable JsonValue value) {
|
||||
var streams = new ArrayList<Supplier<Stream<Map<String, JsonValue>>>>();
|
||||
|
||||
this.patterns.reversed().forEach((keyExpression, pattern) -> streams.add(() -> {
|
||||
var keyStream = keyExpression.evaluate(context);
|
||||
return keyStream.flatMap(key -> pattern.bind(context, JsonMath.index(value, key)));
|
||||
}));
|
||||
|
||||
return Util.cross(streams).map(list -> {
|
||||
var map = new HashMap<String, JsonValue>();
|
||||
list.reversed().forEach(map::putAll);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
var out = new StringJoiner(", ", "{", "}");
|
||||
patterns.forEach((key, pattern) -> out.add(key + ": " + pattern));
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -42,7 +43,7 @@ public record JQAssignment(@NotNull JQExpression target, @NotNull JQExpression v
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<JsonValue> evaluate(@NotNull Context context) {
|
||||
throw new UnsupportedOperationException("not yet implemented");
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -14,7 +15,7 @@ public record JQAssignmentCoerce(@NotNull JQExpression target, @NotNull JQExpres
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<JsonValue> evaluate(@NotNull Context context) {
|
||||
throw new UnsupportedOperationException("not yet implemented");
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQAssignmentPipe(@NotNull JQExpression target, @NotNull JQExpression value) implements JQExpression {
|
||||
|
||||
@ -14,7 +14,7 @@ public record JQAssignmentPipe(@NotNull JQExpression target, @NotNull JQExpressi
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<JsonValue> evaluate(@NotNull Context context) {
|
||||
throw new UnsupportedOperationException("not yet implemented");
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import eu.jonahbauer.json.query.util.Util;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -10,7 +11,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQBinaryExpression(@NotNull JQExpression first, @NotNull JQExpression second, @NotNull Operator operator) implements JQExpression {
|
||||
public static @NotNull JQBinaryExpression add(@NotNull JQExpression first, @NotNull JQExpression second) {
|
||||
@ -64,7 +64,7 @@ public record JQBinaryExpression(@NotNull JQExpression first, @NotNull JQExpress
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Util.crossReversed(List.of(first, second), context)
|
||||
.map(values -> operator.apply(values.getFirst(), values.getLast()));
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
import eu.jonahbauer.json.JsonBoolean;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQBooleanAndExpression(@NotNull JQExpression first, @NotNull JQExpression second) implements JQExpression {
|
||||
|
||||
@ -16,10 +16,10 @@ public record JQBooleanAndExpression(@NotNull JQExpression first, @NotNull JQExp
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<JsonValue> evaluate(@NotNull Context context) {
|
||||
return first.evaluate(context)
|
||||
.flatMap(value -> JsonMath.isFalsy(value)
|
||||
? Stream.of(JsonBoolean.FALSE)
|
||||
? Generator.of(JsonBoolean.FALSE)
|
||||
: second.evaluate(context).map(JsonMath::isTruthy).map(JsonBoolean::valueOf)
|
||||
);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
import eu.jonahbauer.json.JsonBoolean;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -16,10 +17,10 @@ public record JQBooleanOrExpression(@NotNull JQExpression first, @NotNull JQExpr
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<JsonValue> evaluate(@NotNull Context context) {
|
||||
return first.evaluate(context)
|
||||
.flatMap(value -> JsonMath.isTruthy(value)
|
||||
? Stream.of(JsonBoolean.TRUE)
|
||||
? Generator.of(JsonBoolean.TRUE)
|
||||
: second.evaluate(context).map(JsonMath::isTruthy).map(JsonBoolean::valueOf)
|
||||
);
|
||||
}
|
||||
|
@ -3,30 +3,33 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
import eu.jonahbauer.json.*;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.JsonQueryException;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import eu.jonahbauer.json.query.parser.JQParser;
|
||||
import eu.jonahbauer.json.query.parser.ast.JQExpression.Context;
|
||||
import eu.jonahbauer.json.query.util.Util;
|
||||
import lombok.SneakyThrows;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Gatherer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public enum JQBuiltIn implements JQInvocable {
|
||||
ABS(0, (context, _) -> context.stream().map(JsonMath::abs)),
|
||||
ABS(0, Implementation.map$V(JsonMath::abs)),
|
||||
ADD(0, (context, _) -> context.stream().map(value -> JsonMath.values(value).reduce(null, JsonMath::add))),
|
||||
NOT(0, (context, _) -> context.stream().map(JsonMath::not)),
|
||||
NOT(0, Implementation.map$V(JsonMath::not)),
|
||||
|
||||
// error handling
|
||||
ERROR$0(0, (context, _) -> context.stream().flatMap(JsonMath::error)),
|
||||
ERROR$1(1, (context, args) -> args.getFirst().evaluate(context).flatMap(JsonMath::error)),
|
||||
HALT(0, (_, _) -> Stream.of((JsonValue) null).flatMap(_ -> JsonMath.halt())),
|
||||
HALT(0, (_, _) -> Generator.of((JsonValue) null).flatMap(_ -> JsonMath.halt())),
|
||||
HALT_ERROR$0(0, (context, _) -> context.stream().flatMap(JsonMath::halt)),
|
||||
HALT_ERROR$1(1, (context, args) -> args.getFirst().evaluate(context).flatMap(value -> JsonMath.halt(context.root(), value))),
|
||||
|
||||
// stream operations
|
||||
EMPTY(0, (_, _) -> Stream.empty()),
|
||||
EMPTY(0, (_, _) -> Generator.empty()),
|
||||
RANGE$1(1, (context, args) -> args.getFirst().evaluate(context).flatMap(JsonMath::range)),
|
||||
RANGE$2(2, (context, args) -> Util.cross(args, context).flatMap(bounds -> JsonMath.range(bounds.get(0), bounds.get(1)))),
|
||||
RANGE$3(3, (context, args) -> Util.cross(args, context).flatMap(bounds -> JsonMath.range(bounds.get(0), bounds.get(1), bounds.get(2)))),
|
||||
@ -34,113 +37,84 @@ public enum JQBuiltIn implements JQInvocable {
|
||||
FIRST$1(1, (context, args) -> JsonMath.first(args.getFirst().evaluate(context))),
|
||||
LAST$1(1, (context, args) -> JsonMath.last(args.getFirst().evaluate(context))),
|
||||
NTH$2(2, (context, args) -> args.getFirst().evaluate(context).flatMap(n -> JsonMath.nth(args.getLast().evaluate(context), n))),
|
||||
SELECT(1, Implementation.mapF$F(JsonMath::select)),
|
||||
|
||||
// iterable operations
|
||||
MAP(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.map(value, filter));
|
||||
}),
|
||||
MAP_VALUES(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.mapValues(value, filter));
|
||||
}),
|
||||
KEYS(0, (context, _) -> context.stream().map(JsonMath::keys)),
|
||||
KEYS_UNSORTED(0, (context, _) -> context.stream().map(JsonMath::keysUnsorted)),
|
||||
HAS(1, (context, args) -> args.getFirst().evaluate(context).map(index -> JsonMath.has(context.root(), index))),
|
||||
IN(1, (context, args) -> args.getFirst().evaluate(context).map(value -> JsonMath.in(context.root(), value))),
|
||||
FIRST$0(0, (context, _) -> context.stream().map(value -> JsonMath.index(value, JsonNumber.ZERO))),
|
||||
LAST$0(0, (context, _) -> context.stream().map(value -> JsonMath.index(value, new JsonNumber(-1)))),
|
||||
NTH$1(1, (context, args) -> args.getFirst().evaluate(context).map(index -> JsonMath.index(context.root(), index))),
|
||||
ANY$0(0, (context, _) -> context.stream().map(JsonMath::any)),
|
||||
ANY$1(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.any(value, filter));
|
||||
}),
|
||||
ANY$2(2, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return Util.lazy(() -> JsonMath.any(args.getFirst().evaluate(context), filter));
|
||||
}),
|
||||
ALL$0(0, (context, _) -> context.stream().map(JsonMath::all)),
|
||||
ALL$1(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.all(value, filter));
|
||||
}),
|
||||
ALL$2(2, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return Util.lazy(() -> JsonMath.all(args.getFirst().evaluate(context), filter));
|
||||
}),
|
||||
FLATTEN$0(0, (context, _) -> context.stream().map(JsonMath::flatten)),
|
||||
FLATTEN$1(1, (context, args) -> context.stream().flatMap(value -> args.getFirst().evaluate(context).map(depth -> JsonMath.flatten(value, depth)))),
|
||||
SORT(0, (context, _) -> context.stream().map(JsonMath::sort)),
|
||||
SORT_BY(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.sort(value, filter));
|
||||
}),
|
||||
MIN(0, (context, _) -> context.stream().map(JsonMath::min)),
|
||||
MIN_BY(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.min(value, filter));
|
||||
}),
|
||||
MAX(0, (context, _) -> context.stream().map(JsonMath::max)),
|
||||
MAX_BY(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.max(value, filter));
|
||||
}),
|
||||
UNIQUE(0, (context, _) -> context.stream().map(JsonMath::unique)),
|
||||
UNIQUE_BY(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.unique(value, filter));
|
||||
}),
|
||||
GROUP_BY(1, (context, args) -> {
|
||||
var filter = args.getFirst().bind(context);
|
||||
return context.stream().map(value -> JsonMath.group(value, filter));
|
||||
}),
|
||||
REVERSE(0, (context, _) -> context.stream().map(JsonMath::reverse)),
|
||||
CONTAINS(1, (context, args) -> args.getFirst().evaluate(context).map(content -> JsonMath.contains(context.root(), content))),
|
||||
INDICES(1, (context, args) -> args.getFirst().evaluate(context).map(content -> JsonMath.indices(context.root(), content))),
|
||||
INDEX(1, (context, args) -> args.getFirst().evaluate(context).map(content -> JsonMath.firstindex(context.root(), content))),
|
||||
RINDEX(1, (context, args) -> args.getFirst().evaluate(context).map(content -> JsonMath.lastindex(context.root(), content))),
|
||||
INSIDE(1, (context, args) -> args.getFirst().evaluate(context).map(content -> JsonMath.inside(context.root(), content))),
|
||||
COMBINATIONS$0(0, (context, _) -> context.stream().flatMap(JsonMath::combinations)),
|
||||
COMBINATIONS$1(1, (context, args) -> args.getFirst().evaluate(context).flatMap(n -> JsonMath.combinations(context.root(), n))),
|
||||
BSEARCH(1, (context, args) -> args.getFirst().evaluate(context).map(value -> JsonMath.bsearch(context.root(), value))),
|
||||
TRANSPOSE(0, (context, _) -> context.stream().map(JsonMath::transpose)),
|
||||
MAP(1, Implementation.mapF$V(JsonMath::map)),
|
||||
MAP_VALUES(1, Implementation.mapF$V(JsonMath::mapValues)),
|
||||
KEYS(0, Implementation.map$V(JsonMath::keys)),
|
||||
KEYS_UNSORTED(0, Implementation.map$V(JsonMath::keysUnsorted)),
|
||||
HAS(1, Implementation.mapV$V(JsonMath::has)),
|
||||
IN(1, Implementation.mapV$V(JsonMath::in)),
|
||||
FIRST$0(0, Implementation.map$V(JsonMath::first)),
|
||||
LAST$0(0, Implementation.map$V(JsonMath::last)),
|
||||
NTH$1(1, Implementation.mapV$V(JsonMath::index)),
|
||||
ANY$0(0, Implementation.map$V(JsonMath::any)),
|
||||
ANY$1(1, Implementation.mapF$V(JsonMath::any)),
|
||||
ANY$2(2, Implementation.mapFF$V((root, gen, filter) -> JsonMath.any(gen.apply(root), filter))),
|
||||
ALL$0(0, Implementation.map$V(JsonMath::all)),
|
||||
ALL$1(1, Implementation.mapF$V(JsonMath::all)),
|
||||
ALL$2(2, Implementation.mapFF$V((root, gen, filter) -> JsonMath.all(gen.apply(root), filter))),
|
||||
FLATTEN$0(0, Implementation.map$V(JsonMath::flatten)),
|
||||
FLATTEN$1(1, Implementation.mapV$V(JsonMath::flatten)),
|
||||
SORT(0, Implementation.map$V(JsonMath::sort)),
|
||||
SORT_BY(1, Implementation.mapF$V(JsonMath::sort)),
|
||||
MIN(0, Implementation.map$V(JsonMath::min)),
|
||||
MIN_BY(1, Implementation.mapF$V(JsonMath::min)),
|
||||
MAX(0, Implementation.map$V(JsonMath::max)),
|
||||
MAX_BY(1, Implementation.mapF$V(JsonMath::max)),
|
||||
UNIQUE(0, Implementation.map$V(JsonMath::unique)),
|
||||
UNIQUE_BY(1, Implementation.mapF$V(JsonMath::unique)),
|
||||
GROUP_BY(1, Implementation.mapF$V(JsonMath::group)),
|
||||
REVERSE(0, Implementation.map$V(JsonMath::reverse)),
|
||||
CONTAINS(1, Implementation.mapV$V(JsonMath::contains)),
|
||||
INDICES(1, Implementation.mapV$V(JsonMath::indices)),
|
||||
INDEX(1, Implementation.mapV$V(JsonMath::firstindex)),
|
||||
RINDEX(1, Implementation.mapV$V(JsonMath::lastindex)),
|
||||
INSIDE(1, Implementation.mapV$V(JsonMath::inside)),
|
||||
COMBINATIONS$0(0, Implementation.map$F(JsonMath::combinations)),
|
||||
COMBINATIONS$1(1, Implementation.mapV$F(JsonMath::combinations)),
|
||||
BSEARCH(1, Implementation.mapV$V(JsonMath::bsearch)),
|
||||
TRANSPOSE(0, Implementation.map$V(JsonMath::transpose)),
|
||||
TO_ENTRIES(0, Implementation.map$V(JsonMath::toEntries)),
|
||||
FROM_ENTRIES(0, Implementation.map$V(JsonMath::fromEntries)),
|
||||
WITH_ENTRIES(1, Implementation.mapF$V(JsonMath::withEntries)),
|
||||
|
||||
// filters
|
||||
ARRAYS(0, (context, _) -> context.stream().filter(JsonMath::isArray)),
|
||||
OBJECTS(0, (context, _) -> context.stream().filter(JsonMath::isObject)),
|
||||
ITERABLES(0, (context, _) -> context.stream().filter(JsonMath::isIterable)),
|
||||
BOOLEANS(0, (context, _) -> context.stream().filter(JsonMath::isBoolean)),
|
||||
NUMBERS(0, (context, _) -> context.stream().filter(JsonMath::isNumber)),
|
||||
NORMALS(0, (context, _) -> context.stream().filter(JsonMath::isNormal)),
|
||||
FINITES(0, (context, _) -> context.stream().filter(JsonMath::isFinite)),
|
||||
STRINGS(0, (context, _) -> context.stream().filter(JsonMath::isString)),
|
||||
NULLS(0, (context, _) -> context.stream().filter(JsonMath::isNull)),
|
||||
VALUES(0, (context, _) -> context.stream().filter(JsonMath::isValue)),
|
||||
SCALARS(0, (context, _) -> context.stream().filter(JsonMath::isScalar)),
|
||||
ARRAYS(0, Implementation.filter(JsonMath::isArray0)),
|
||||
OBJECTS(0, Implementation.filter(JsonMath::isObject0)),
|
||||
ITERABLES(0, Implementation.filter(JsonMath::isIterable0)),
|
||||
BOOLEANS(0, Implementation.filter(JsonMath::isBoolean0)),
|
||||
NUMBERS(0, Implementation.filter(JsonMath::isNumber0)),
|
||||
NORMALS(0, Implementation.filter(JsonMath::isNormal0)),
|
||||
FINITES(0, Implementation.filter(JsonMath::isFinite0)),
|
||||
STRINGS(0, Implementation.filter(JsonMath::isString0)),
|
||||
NULLS(0, Implementation.filter(JsonMath::isNull0)),
|
||||
VALUES(0, Implementation.filter(JsonMath::isValue0)),
|
||||
SCALARS(0, Implementation.filter(JsonMath::isScalar0)),
|
||||
|
||||
// checks
|
||||
ISINFINITE(0, (context, _) -> context.stream().map(JsonMath::isInfinite).map(JsonBoolean::valueOf)),
|
||||
ISNAN(0, (context, _) -> context.stream().map(JsonMath::isNan).map(JsonBoolean::valueOf)),
|
||||
ISFINITE(0, (context, _) -> context.stream().map(JsonMath::isFinite).map(JsonBoolean::valueOf)),
|
||||
ISNORMAL(0, (context, _) -> context.stream().map(JsonMath::isNormal).map(JsonBoolean::valueOf)),
|
||||
ISEMPTY(1, (context, args) -> Util.lazy(() -> JsonBoolean.valueOf(JsonMath.isEmpty(args.getFirst().evaluate(context))))),
|
||||
ISINFINITE(0, Implementation.map$V(JsonMath::isInfinite)),
|
||||
ISNAN(0, Implementation.map$V(JsonMath::isNan)),
|
||||
ISFINITE(0, Implementation.map$V(JsonMath::isFinite)),
|
||||
ISNORMAL(0, Implementation.map$V(JsonMath::isNormal)),
|
||||
ISEMPTY(1, (context, args) -> Generator.of(() -> JsonMath.isEmpty(args.getFirst().evaluate(context)))),
|
||||
|
||||
// string operations
|
||||
TRIM(0, (context, _) -> context.stream().map(JsonMath::trim)),
|
||||
LTRIM(0, (context, _) -> context.stream().map(JsonMath::ltrim)),
|
||||
RTRIM(0, (context, _) -> context.stream().map(JsonMath::rtrim)),
|
||||
LTRIMSTR(1, (context, args) -> args.getFirst().evaluate(context).map(prefix -> JsonMath.ltrimstr(context.root(), prefix))),
|
||||
RTRIMSTR(1, (context, args) -> args.getFirst().evaluate(context).map(suffix -> JsonMath.rtrimstr(context.root(), suffix))),
|
||||
SPLIT$1(1, (context, args) -> args.getFirst().evaluate(context).map(separator -> JsonMath.split(context.root(), separator))),
|
||||
JOIN(1, (context, args) -> args.getFirst().evaluate(context).map(separator -> JsonMath.join(context.root(), separator))),
|
||||
IMPLODE(0, (context, _) -> context.stream().map(JsonMath::implode)),
|
||||
EXPLODE(0, (context, _) -> context.stream().map(JsonMath::explode)),
|
||||
ASCII_UPCASE(0, (context, _) -> context.stream().map(JsonMath::asciiUpcase)),
|
||||
ASCII_DOWNCASE(0, (context, _) -> context.stream().map(JsonMath::asciiDowncase)),
|
||||
UTF8BYTELENGTH(0, (context, _) -> context.stream().map(JsonMath::utf8ByteLength)),
|
||||
STARTSWITH(1, (context, args) -> args.getFirst().evaluate(context).map(prefix -> JsonMath.startswith(context.root(), prefix))),
|
||||
ENDSWITH(1, (context, args) -> args.getFirst().evaluate(context).map(prefix -> JsonMath.endswith(context.root(), prefix))),
|
||||
TRIM(0, Implementation.map$V(JsonMath::trim)),
|
||||
LTRIM(0, Implementation.map$V(JsonMath::ltrim)),
|
||||
RTRIM(0, Implementation.map$V(JsonMath::rtrim)),
|
||||
LTRIMSTR(1, Implementation.mapV$V(JsonMath::ltrimstr)),
|
||||
RTRIMSTR(1, Implementation.mapV$V(JsonMath::rtrimstr)),
|
||||
SPLIT$1(1, Implementation.mapV$V(JsonMath::split)),
|
||||
JOIN(1, Implementation.mapV$V(JsonMath::join)),
|
||||
IMPLODE(0, Implementation.map$V(JsonMath::implode)),
|
||||
EXPLODE(0, Implementation.map$V(JsonMath::explode)),
|
||||
ASCII_UPCASE(0, Implementation.map$V(JsonMath::asciiUpcase)),
|
||||
ASCII_DOWNCASE(0, Implementation.map$V(JsonMath::asciiDowncase)),
|
||||
UTF8BYTELENGTH(0, Implementation.map$V(JsonMath::utf8ByteLength)),
|
||||
STARTSWITH(1, Implementation.mapV$V(JsonMath::startswith)),
|
||||
ENDSWITH(1, Implementation.mapV$V(JsonMath::endswith)),
|
||||
|
||||
// regex
|
||||
TEST$1(1, JsonMath::test),
|
||||
@ -160,76 +134,83 @@ public enum JQBuiltIn implements JQInvocable {
|
||||
GSUB$3(3, JsonMath::gsub),
|
||||
|
||||
// conversions
|
||||
TYPE(0, (context, _) -> context.stream().map(JsonMath::type)),
|
||||
TOJSON(0, (context, _) -> context.stream().map(JsonMath::tojson)),
|
||||
TOSTRING(0, (context, _) -> context.stream().map(JsonMath::tostring)),
|
||||
TONUMBER(0, (context, _) -> context.stream().map(JsonMath::tonumber)),
|
||||
FROMJSON(0, (context, _) -> context.stream().map(JsonMath::fromjson)),
|
||||
TYPE(0, Implementation.map$V(JsonMath::type)),
|
||||
TOJSON(0, Implementation.map$V(JsonMath::tojson)),
|
||||
TOSTRING(0, Implementation.map$V(JsonMath::tostring)),
|
||||
TONUMBER(0, Implementation.map$V(JsonMath::tonumber)),
|
||||
FROMJSON(0, Implementation.map$V(JsonMath::fromjson)),
|
||||
|
||||
// time
|
||||
FROMDATEISO8601(0, Implementation.map$V(JsonMath::fromdateiso8601)),
|
||||
FROMDATE(0, Implementation.map$V(JsonMath::fromdate)),
|
||||
TODATEISO8601(0, Implementation.map$V(JsonMath::todateiso8601)),
|
||||
TODATE(0, Implementation.map$V(JsonMath::todate)),
|
||||
NOW(0, Implementation.map$V(_ -> JsonNumber.valueOf(System.currentTimeMillis()))),
|
||||
|
||||
// misc
|
||||
LENGTH(0, (context, _) -> context.stream().map(JsonMath::length)),
|
||||
REPEAT(1, (context, args) -> Stream.generate(() -> args.getFirst().evaluate(context)).flatMap(Function.identity())),
|
||||
LENGTH(0, Implementation.map$V(JsonMath::length)),
|
||||
REPEAT(1, (context, args) -> JsonMath.repeat(() -> args.getFirst().evaluate(context))),
|
||||
|
||||
// math library
|
||||
ACOS(0, (context, _) -> context.stream().map(JsonMath::acos)),
|
||||
ACOSH(0, (context, _) -> context.stream().map(JsonMath::acosh)),
|
||||
ASIN(0, (context, _) -> context.stream().map(JsonMath::asin)),
|
||||
ASINH(0, (context, _) -> context.stream().map(JsonMath::asinh)),
|
||||
ATAN(0, (context, _) -> context.stream().map(JsonMath::atan)),
|
||||
ATANH(0, (context, _) -> context.stream().map(JsonMath::atanh)),
|
||||
CBRT(0, (context, _) -> context.stream().map(JsonMath::cbrt)),
|
||||
CEIL(0, (context, _) -> context.stream().map(JsonMath::ceil)),
|
||||
COS(0, (context, _) -> context.stream().map(JsonMath::cos)),
|
||||
COSH(0, (context, _) -> context.stream().map(JsonMath::cosh)),
|
||||
ERF(0, (context, _) -> context.stream().map(JsonMath::erf)),
|
||||
ERFC(0, (context, _) -> context.stream().map(JsonMath::erfc)),
|
||||
EXP(0, (context, _) -> context.stream().map(JsonMath::exp)),
|
||||
EXP10(0, (context, _) -> context.stream().map(JsonMath::exp10)),
|
||||
EXP2(0, (context, _) -> context.stream().map(JsonMath::exp2)),
|
||||
EXPM1(0, (context, _) -> context.stream().map(JsonMath::expm1)),
|
||||
FABS(0, (context, _) -> context.stream().map(JsonMath::fabs)),
|
||||
FLOOR(0, (context, _) -> context.stream().map(JsonMath::floor)),
|
||||
GAMMA(0, (context, _) -> context.stream().map(JsonMath::gamma)),
|
||||
J0(0, (context, _) -> context.stream().map(JsonMath::j0)),
|
||||
J1(0, (context, _) -> context.stream().map(JsonMath::j1)),
|
||||
LGAMMA(0, (context, _) -> context.stream().map(JsonMath::lgamma)),
|
||||
LOG(0, (context, _) -> context.stream().map(JsonMath::log)),
|
||||
LOG10(0, (context, _) -> context.stream().map(JsonMath::log10)),
|
||||
LOG1P(0, (context, _) -> context.stream().map(JsonMath::log1p)),
|
||||
LOG2(0, (context, _) -> context.stream().map(JsonMath::log2)),
|
||||
LOGB(0, (context, _) -> context.stream().map(JsonMath::logb)),
|
||||
NEARBYINT(0, (context, _) -> context.stream().map(JsonMath::nearbyint)),
|
||||
RINT(0, (context, _) -> context.stream().map(JsonMath::rint)),
|
||||
ROUND(0, (context, _) -> context.stream().map(JsonMath::round)),
|
||||
SIGNIFICAND(0, (context, _) -> context.stream().map(JsonMath::significand)),
|
||||
SIN(0, (context, _) -> context.stream().map(JsonMath::sin)),
|
||||
SINH(0, (context, _) -> context.stream().map(JsonMath::sinh)),
|
||||
SQRT(0, (context, _) -> context.stream().map(JsonMath::sqrt)),
|
||||
TAN(0, (context, _) -> context.stream().map(JsonMath::tan)),
|
||||
TANH(0, (context, _) -> context.stream().map(JsonMath::tanh)),
|
||||
TGAMMA(0, (context, _) -> context.stream().map(JsonMath::tgamma)),
|
||||
TRUNC(0, (context, _) -> context.stream().map(JsonMath::trunc)),
|
||||
Y0(0, (context, _) -> context.stream().map(JsonMath::y0)),
|
||||
Y1(0, (context, _) -> context.stream().map(JsonMath::y1)),
|
||||
ATAN2(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.atan2(values.get(0), values.get(1)))),
|
||||
COPYSIGN(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.copysign(values.get(0), values.get(1)))),
|
||||
DREM(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.drem(values.get(0), values.get(1)))),
|
||||
FDIM(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.fdim(values.get(0), values.get(1)))),
|
||||
FMAX(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.fmax(values.get(0), values.get(1)))),
|
||||
FMIN(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.fmin(values.get(0), values.get(1)))),
|
||||
FMOD(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.fmod(values.get(0), values.get(1)))),
|
||||
FREXP(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.frexp(values.get(0), values.get(1)))),
|
||||
HYPOT(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.hypot(values.get(0), values.get(1)))),
|
||||
JN(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.jn(values.get(0), values.get(1)))),
|
||||
LDEXP(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.ldexp(values.get(0), values.get(1)))),
|
||||
MODF(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.modf(values.get(0), values.get(1)))),
|
||||
NEXTAFTER(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.nextafter(values.get(0), values.get(1)))),
|
||||
NEXTTOWARD(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.nexttoward(values.get(0), values.get(1)))),
|
||||
POW(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.pow(values.get(0), values.get(1)))),
|
||||
REMAINDER(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.remainder(values.get(0), values.get(1)))),
|
||||
SCALB(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.scalb(values.get(0), values.get(1)))),
|
||||
SCALBLN(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.scalbln(values.get(0), values.get(1)))),
|
||||
YN(2, (context, args) -> Util.cross(args, context).map(values -> JsonMath.yn(values.get(0), values.get(1)))),
|
||||
ACOS(0, Implementation.map$V(JsonMath::acos)),
|
||||
ACOSH(0, Implementation.map$V(JsonMath::acosh)),
|
||||
ASIN(0, Implementation.map$V(JsonMath::asin)),
|
||||
ASINH(0, Implementation.map$V(JsonMath::asinh)),
|
||||
ATAN(0, Implementation.map$V(JsonMath::atan)),
|
||||
ATANH(0, Implementation.map$V(JsonMath::atanh)),
|
||||
CBRT(0, Implementation.map$V(JsonMath::cbrt)),
|
||||
CEIL(0, Implementation.map$V(JsonMath::ceil)),
|
||||
COS(0, Implementation.map$V(JsonMath::cos)),
|
||||
COSH(0, Implementation.map$V(JsonMath::cosh)),
|
||||
ERF(0, Implementation.map$V(JsonMath::erf)),
|
||||
ERFC(0, Implementation.map$V(JsonMath::erfc)),
|
||||
EXP(0, Implementation.map$V(JsonMath::exp)),
|
||||
EXP10(0, Implementation.map$V(JsonMath::exp10)),
|
||||
EXP2(0, Implementation.map$V(JsonMath::exp2)),
|
||||
EXPM1(0, Implementation.map$V(JsonMath::expm1)),
|
||||
FABS(0, Implementation.map$V(JsonMath::fabs)),
|
||||
FLOOR(0, Implementation.map$V(JsonMath::floor)),
|
||||
GAMMA(0, Implementation.map$V(JsonMath::gamma)),
|
||||
J0(0, Implementation.map$V(JsonMath::j0)),
|
||||
J1(0, Implementation.map$V(JsonMath::j1)),
|
||||
LGAMMA(0, Implementation.map$V(JsonMath::lgamma)),
|
||||
LOG(0, Implementation.map$V(JsonMath::log)),
|
||||
LOG10(0, Implementation.map$V(JsonMath::log10)),
|
||||
LOG1P(0, Implementation.map$V(JsonMath::log1p)),
|
||||
LOG2(0, Implementation.map$V(JsonMath::log2)),
|
||||
LOGB(0, Implementation.map$V(JsonMath::logb)),
|
||||
NEARBYINT(0, Implementation.map$V(JsonMath::nearbyint)),
|
||||
RINT(0, Implementation.map$V(JsonMath::rint)),
|
||||
ROUND(0, Implementation.map$V(JsonMath::round)),
|
||||
SIGNIFICAND(0, Implementation.map$V(JsonMath::significand)),
|
||||
SIN(0, Implementation.map$V(JsonMath::sin)),
|
||||
SINH(0, Implementation.map$V(JsonMath::sinh)),
|
||||
SQRT(0, Implementation.map$V(JsonMath::sqrt)),
|
||||
TAN(0, Implementation.map$V(JsonMath::tan)),
|
||||
TANH(0, Implementation.map$V(JsonMath::tanh)),
|
||||
TGAMMA(0, Implementation.map$V(JsonMath::tgamma)),
|
||||
TRUNC(0, Implementation.map$V(JsonMath::trunc)),
|
||||
Y0(0, Implementation.map$V(JsonMath::y0)),
|
||||
Y1(0, Implementation.map$V(JsonMath::y1)),
|
||||
ATAN2(2, Implementation.mapVV$V((_, a, b) -> JsonMath.atan2(a, b))),
|
||||
COPYSIGN(2, Implementation.mapVV$V((_, a, b) -> JsonMath.copysign(a, b))),
|
||||
DREM(2, Implementation.mapVV$V((_, a, b) -> JsonMath.drem(a, b))),
|
||||
FDIM(2, Implementation.mapVV$V((_, a, b) -> JsonMath.fdim(a, b))),
|
||||
FMAX(2, Implementation.mapVV$V((_, a, b) -> JsonMath.fmax(a, b))),
|
||||
FMIN(2, Implementation.mapVV$V((_, a, b) -> JsonMath.fmin(a, b))),
|
||||
FMOD(2, Implementation.mapVV$V((_, a, b) -> JsonMath.fmod(a, b))),
|
||||
FREXP(2, Implementation.mapVV$V((_, a, b) -> JsonMath.frexp(a, b))),
|
||||
HYPOT(2, Implementation.mapVV$V((_, a, b) -> JsonMath.hypot(a, b))),
|
||||
JN(2, Implementation.mapVV$V((_, a, b) -> JsonMath.jn(a, b))),
|
||||
LDEXP(2, Implementation.mapVV$V((_, a, b) -> JsonMath.ldexp(a, b))),
|
||||
MODF(2, Implementation.mapVV$V((_, a, b) -> JsonMath.modf(a, b))),
|
||||
NEXTAFTER(2, Implementation.mapVV$V((_, a, b) -> JsonMath.nextafter(a, b))),
|
||||
NEXTTOWARD(2, Implementation.mapVV$V((_, a, b) -> JsonMath.nexttoward(a, b))),
|
||||
POW(2, Implementation.mapVV$V((_, a, b) -> JsonMath.pow(a, b))),
|
||||
REMAINDER(2, Implementation.mapVV$V((_, a, b) -> JsonMath.remainder(a, b))),
|
||||
SCALB(2, Implementation.mapVV$V((_, a, b) -> JsonMath.scalb(a, b))),
|
||||
SCALBLN(2, Implementation.mapVV$V((_, a, b) -> JsonMath.scalbln(a, b))),
|
||||
YN(2, Implementation.mapVV$V((_, a, b) -> JsonMath.yn(a, b))),
|
||||
FMA(3, (context, args) -> Util.cross(args, context).map(values -> JsonMath.fma(values.get(0), values.get(1), values.get(2))))
|
||||
;
|
||||
|
||||
@ -239,38 +220,159 @@ public enum JQBuiltIn implements JQInvocable {
|
||||
Function.identity()
|
||||
));
|
||||
|
||||
private final @NotNull String identifier;
|
||||
private final int arity;
|
||||
private final @NotNull Implementation implementation;
|
||||
private final @NotNull JQInvocable implementation;
|
||||
|
||||
JQBuiltIn(@NotNull String body) {
|
||||
this(List.of(), parse(body));
|
||||
}
|
||||
|
||||
JQBuiltIn(@NotNull String arg0, @NotNull String body) {
|
||||
this(List.of(arg0), parse(body));
|
||||
}
|
||||
|
||||
JQBuiltIn(@NotNull String arg0, @NotNull String arg1, @NotNull String body) {
|
||||
this(List.of(arg0, arg1), parse(body));
|
||||
}
|
||||
|
||||
JQBuiltIn(@NotNull String arg0, @NotNull String arg1, @NotNull String arg2, @NotNull String body) {
|
||||
this(List.of(arg0, arg1, arg2), parse(body));
|
||||
}
|
||||
|
||||
JQBuiltIn(@NotNull List<@NotNull String> params, @NotNull JQExpression body) {
|
||||
this.implementation = new JQFunction(getIdentifier(name()), params, body);
|
||||
}
|
||||
|
||||
JQBuiltIn(int arity, @NotNull Implementation implementation) {
|
||||
var identifier = this.name().toLowerCase(Locale.ROOT);
|
||||
this.implementation = new InvocableDelegate(getIdentifier(name()), arity, implementation);
|
||||
}
|
||||
|
||||
@SneakyThrows(IOException.class)
|
||||
private static @NotNull JQExpression parse(@NotNull String expression) {
|
||||
return new JQParser(expression).parseExpression();
|
||||
}
|
||||
|
||||
private static @NotNull String getIdentifier(@NotNull String name) {
|
||||
var identifier = name.toLowerCase(Locale.ROOT);
|
||||
var idx = identifier.lastIndexOf("$");
|
||||
if (idx != -1) identifier = identifier.substring(0, idx);
|
||||
|
||||
this.identifier = identifier;
|
||||
this.arity = arity;
|
||||
this.implementation = implementation;
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> invoke(JQExpression.@NotNull Context context, @NotNull List<@NotNull JQExpression> args) {
|
||||
if (args.size() != arity) throw new JsonQueryException("invalid argument count");
|
||||
public @NotNull Generator<@Nullable JsonValue> invoke(@NotNull Context context, @NotNull List<@NotNull JQExpression> args) {
|
||||
return implementation.invoke(context, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String identifier() {
|
||||
return identifier;
|
||||
return implementation.identifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int arity() {
|
||||
return arity;
|
||||
return implementation.arity();
|
||||
}
|
||||
|
||||
private record InvocableDelegate(
|
||||
@NotNull String identifier, int arity,
|
||||
@NotNull Implementation delegate
|
||||
) implements JQInvocable {
|
||||
|
||||
@Override
|
||||
public @NotNull Generator<@Nullable JsonValue> invoke(@NotNull Context context, @NotNull List<@NotNull JQExpression> args) {
|
||||
if (args.size() != arity) throw new JsonQueryException("invalid argument count");
|
||||
return delegate.invoke(context, args);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Implementation {
|
||||
@NotNull Stream<@Nullable JsonValue> invoke(@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args);
|
||||
@NotNull Generator<@Nullable JsonValue> invoke(@NotNull Context context, @NotNull List<@NotNull JQExpression> args);
|
||||
|
||||
// format: map<args>$<return>
|
||||
// where <args> and <return> use V for value, F for filter
|
||||
|
||||
/**
|
||||
* Produces a zero-arg implementation applying the function to the current value.
|
||||
*/
|
||||
static @NotNull Implementation map$F(@NotNull Function<? super @Nullable JsonValue, ? extends @NotNull Generator<? extends @Nullable JsonValue>> operator) {
|
||||
return (context, _) -> context.stream().flatMap(operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a zero-arg implementation applying the function to the current value.
|
||||
*/
|
||||
static @NotNull Implementation map$V(@NotNull Function<? super @Nullable JsonValue, ? extends @Nullable JsonValue> operator) {
|
||||
return (context, _) -> context.stream().map(operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a one-arg implementation applying the function to the current value and the first argument
|
||||
* (passed by value).
|
||||
*/
|
||||
static @NotNull Implementation mapV$V(@NotNull BiFunction<? super @Nullable JsonValue, ? super @Nullable JsonValue, ? extends @Nullable JsonValue> function) {
|
||||
return (context, args) -> {
|
||||
var arg = args.getFirst();
|
||||
return context.stream().flatMap(root -> arg.evaluate(context).map(value -> function.apply(root, value)));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a one-arg implementation applying the function to the current value and the first argument
|
||||
* (passed by value).
|
||||
*/
|
||||
static @NotNull Implementation mapV$F(@NotNull BiFunction<? super @Nullable JsonValue, ? super @Nullable JsonValue, ? extends @NotNull Generator<? extends @Nullable JsonValue>> function) {
|
||||
return (context, args) -> {
|
||||
var arg = args.getFirst();
|
||||
return context.stream().flatMap(root -> arg.evaluate(context).flatMap(value -> function.apply(root, value)));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a one-arg implementation applying the function to the current value and the first argument
|
||||
* (passed as filter).
|
||||
*/
|
||||
static @NotNull Implementation mapF$F(@NotNull BiFunction<? super @Nullable JsonValue, ? super @NotNull JQFilter, ? extends @NotNull Generator<? extends @Nullable JsonValue>> function) {
|
||||
return (context, args) -> {
|
||||
var arg = args.getFirst().bind(context);
|
||||
return context.stream().flatMap(root -> function.apply(root, arg));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a one-arg implementation applying the function to the current value and the first argument
|
||||
* (passed as filter).
|
||||
*/
|
||||
static @NotNull Implementation mapF$V(@NotNull BiFunction<? super @Nullable JsonValue, ? super @NotNull JQFilter, ? extends @Nullable JsonValue> function) {
|
||||
return (context, args) -> {
|
||||
var arg = args.getFirst().bind(context);
|
||||
return context.stream().map(root -> function.apply(root, arg));
|
||||
};
|
||||
}
|
||||
|
||||
static @NotNull Implementation mapVV$V(@NotNull TriFunction<? super @Nullable JsonValue, ? super @Nullable JsonValue, ? super @Nullable JsonValue, ? extends @Nullable JsonValue> function) {
|
||||
return (context, args) -> {
|
||||
var arg0 = args.get(0);
|
||||
var arg1 = args.get(1);
|
||||
return context.stream().flatMap(root -> arg0.evaluate(context).flatMap(value0 -> arg1.evaluate(context).map(value1 -> function.apply(root, value0, value1))));
|
||||
};
|
||||
}
|
||||
|
||||
static @NotNull Implementation mapFF$V(@NotNull TriFunction<? super @Nullable JsonValue, ? super @NotNull JQFilter, ? super @NotNull JQFilter, ? extends @Nullable JsonValue> function) {
|
||||
return (context, args) -> {
|
||||
var arg0 = args.get(0).bind(context);
|
||||
var arg1 = args.get(1).bind(context);
|
||||
return context.stream().map(root -> function.apply(root, arg0, arg1));
|
||||
};
|
||||
}
|
||||
|
||||
static @NotNull Implementation filter(@NotNull Predicate<@Nullable JsonValue> predicate) {
|
||||
return (context, _) -> context.stream().filter(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface TriFunction<S, T, U, R> {
|
||||
R apply(S s, T t, U u);
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public record JQCommaExpression(@NotNull JQExpression first, @NotNull JQExpression second) implements JQExpression {
|
||||
public JQCommaExpression {
|
||||
@ -16,34 +14,8 @@ public record JQCommaExpression(@NotNull JQExpression first, @NotNull JQExpressi
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
var it = new Iterator<JsonValue>() {
|
||||
private Iterator<JsonValue> delegate;
|
||||
private Queue<Supplier<Iterator<JsonValue>>> queue = new LinkedList<>(List.of(
|
||||
() -> first.evaluate(context).iterator(),
|
||||
() -> second.evaluate(context).iterator()
|
||||
));
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (delegate == null) delegate = queue.remove().get();
|
||||
while (!delegate.hasNext() && !queue.isEmpty()) {
|
||||
delegate = queue.remove().get();
|
||||
}
|
||||
return delegate.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonValue next() {
|
||||
if (delegate == null) delegate = queue.remove().get();
|
||||
while (!delegate.hasNext() && !queue.isEmpty()) {
|
||||
delegate = queue.remove().get();
|
||||
}
|
||||
return delegate.next();
|
||||
}
|
||||
};
|
||||
var spliterator = Spliterators.spliteratorUnknownSize(it, Spliterator.ORDERED);
|
||||
return StreamSupport.stream(spliterator, false);
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Generator.concat(first.evaluate(context), second.evaluate(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,16 +1,15 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQConstant(@Nullable JsonValue value) implements JQExpression {
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Stream.of(value);
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Generator.of(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,16 +2,16 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonQueryException;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface JQExpression {
|
||||
|
||||
@NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context);
|
||||
@NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context);
|
||||
|
||||
boolean isConstant();
|
||||
|
||||
@ -82,8 +82,8 @@ public interface JQExpression {
|
||||
return new Context(root, v, functions);
|
||||
}
|
||||
|
||||
public @NotNull Stream<@Nullable JsonValue> stream() {
|
||||
return Stream.of(root());
|
||||
public @NotNull Generator<@Nullable JsonValue> stream() {
|
||||
return Generator.of(root());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface JQFilter extends Function<@Nullable JsonValue, @NotNull Stream<@Nullable JsonValue>> {
|
||||
public interface JQFilter extends Function<@Nullable JsonValue, @NotNull Generator<@Nullable JsonValue>> {
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonQueryException;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQFunction(@NotNull String identifier, @NotNull List<@NotNull String> params, @NotNull JQExpression body) implements JQInvocable {
|
||||
public JQFunction {
|
||||
@ -15,7 +16,9 @@ public record JQFunction(@NotNull String identifier, @NotNull List<@NotNull Stri
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> invoke(@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> arguments) {
|
||||
public @NotNull Generator<@Nullable JsonValue> invoke(@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> arguments) {
|
||||
if (arguments.size() != params.size()) throw new JsonQueryException("invalid argument count");
|
||||
|
||||
var expression = body;
|
||||
|
||||
var functions = new ArrayList<JQFunction>();
|
||||
@ -24,7 +27,7 @@ public record JQFunction(@NotNull String identifier, @NotNull List<@NotNull Stri
|
||||
if (param.startsWith("$")) {
|
||||
expression = new JQAsExpression(
|
||||
new JQFunctionInvocation(param.substring(1), List.of()),
|
||||
List.of(new JQAsExpression.Pattern.ValuePattern(param)),
|
||||
new JQPatterns(List.of(new JQPatterns.Pattern.ValuePattern(param))),
|
||||
expression
|
||||
);
|
||||
param = param.substring(1);
|
||||
|
@ -1,13 +1,13 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQFunctionInvocation(@NotNull String name, @NotNull List<@NotNull JQExpression> args) implements JQExpression {
|
||||
public JQFunctionInvocation {
|
||||
@ -16,7 +16,7 @@ public record JQFunctionInvocation(@NotNull String name, @NotNull List<@NotNull
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
var function = context.function(name, args.size());
|
||||
return function.invoke(context, args);
|
||||
}
|
||||
|
@ -5,14 +5,13 @@ import eu.jonahbauer.json.JsonString;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.JsonQueryException;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQIndexExpression(@NotNull JQExpression expression, @NotNull JQExpression index, boolean optional) implements JQExpression {
|
||||
|
||||
public JQIndexExpression {
|
||||
Objects.requireNonNull(expression);
|
||||
Objects.requireNonNull(index);
|
||||
@ -31,7 +30,7 @@ public record JQIndexExpression(@NotNull JQExpression expression, @NotNull JQExp
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return expression.evaluate(context).flatMap(value -> index.evaluate(context).mapMulti((index, downstream) -> {
|
||||
try {
|
||||
downstream.accept(JsonMath.index(value, index));
|
||||
|
@ -1,14 +1,15 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface JQInvocable {
|
||||
@NotNull Stream<@Nullable JsonValue> invoke(@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args);
|
||||
@NotNull
|
||||
Generator<@Nullable JsonValue> invoke(@NotNull JQExpression.Context context, @NotNull List<@NotNull JQExpression> args);
|
||||
|
||||
int arity();
|
||||
@NotNull String identifier();
|
||||
|
@ -2,11 +2,11 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQIterateExpression(@NotNull JQExpression expression, boolean optional) implements JQExpression {
|
||||
public JQIterateExpression {
|
||||
@ -14,7 +14,7 @@ public record JQIterateExpression(@NotNull JQExpression expression, boolean opti
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return expression.evaluate(context).flatMap(value -> JsonMath.values(value, optional));
|
||||
}
|
||||
|
||||
|
@ -4,15 +4,14 @@ import eu.jonahbauer.json.JsonNumber;
|
||||
import eu.jonahbauer.json.JsonObject;
|
||||
import eu.jonahbauer.json.JsonString;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQLocExpression(@NotNull String file, int line) implements JQExpression {
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Stream.of(JsonObject.of(
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Generator.of(JsonObject.of(
|
||||
"file", new JsonString(file),
|
||||
"line", new JsonNumber(line)
|
||||
));
|
||||
|
@ -2,6 +2,7 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonMath;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -14,7 +15,7 @@ public record JQNegation(@NotNull JQExpression expression) implements JQExpressi
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return expression.evaluate(context).map(JsonMath::neg);
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQParenthesizedExpression(@NotNull JQExpression expression) implements JQExpression {
|
||||
|
||||
@ -14,7 +14,7 @@ public record JQParenthesizedExpression(@NotNull JQExpression expression) implem
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return expression.evaluate(context);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -14,7 +15,7 @@ public record JQPipeExpression(@NotNull JQExpression first, @NotNull JQExpressio
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return first.evaluate(context).flatMap(value -> second.evaluate(context.withRoot(value)));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -11,4 +13,8 @@ public record JQProgram(
|
||||
@NotNull List<@NotNull JQFunction> functions,
|
||||
@Nullable JQExpression expression
|
||||
) {
|
||||
public @NotNull Generator<@Nullable JsonValue> run(@Nullable JsonValue value) {
|
||||
if (expression == null) return Generator.empty();
|
||||
return expression.evaluate(new JQExpression.Context(value).withFunctions(functions));
|
||||
}
|
||||
}
|
||||
|
@ -3,22 +3,21 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
import eu.jonahbauer.json.JsonArray;
|
||||
import eu.jonahbauer.json.JsonObject;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQRecursionExpression() implements JQExpression {
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return recurse(context.root());
|
||||
}
|
||||
|
||||
private @NotNull Stream<@Nullable JsonValue> recurse(@Nullable JsonValue value) {
|
||||
private @NotNull Generator<@Nullable JsonValue> recurse(@Nullable JsonValue value) {
|
||||
return switch (value) {
|
||||
case JsonArray array -> Stream.concat(Stream.of(array), array.stream().flatMap(this::recurse));
|
||||
case JsonObject object -> Stream.concat(Stream.of(object), object.values().stream().flatMap(this::recurse));
|
||||
case null, default -> Stream.of(value);
|
||||
case JsonArray array -> Generator.concat(Generator.of(array), Generator.from(array).flatMap(this::recurse));
|
||||
case JsonObject object -> Generator.concat(Generator.of(object), Generator.from(object.values()).flatMap(this::recurse));
|
||||
case null, default -> Generator.of(value);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQRootExpression() implements JQExpression {
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return context.stream();
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import eu.jonahbauer.json.JsonNumber;
|
||||
import eu.jonahbauer.json.JsonString;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonQueryException;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import eu.jonahbauer.json.query.util.Util;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -22,29 +23,29 @@ public record JQSliceExpression(@NotNull JQExpression expression, @Nullable JQEx
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return expression.evaluate(context).flatMap(value -> switch (value) {
|
||||
case JsonArray array -> slice(context, "an array slice", array.size(), array::subList);
|
||||
case JsonString string -> slice(context, "a string slice", string.length(), string::subSequence);
|
||||
case null -> Stream.of((JsonValue) null);
|
||||
case null -> Generator.of((JsonValue) null);
|
||||
default -> {
|
||||
if (optional) yield Stream.empty();
|
||||
if (optional) yield Generator.empty();
|
||||
throw new JsonQueryException(STR."Cannot index \{Util.type(value)} with object.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private @NotNull Stream<@Nullable JsonValue> slice(@NotNull Context context, @NotNull String type, int length, @NotNull BiFunction<Integer, Integer, JsonValue> slice) {
|
||||
private @NotNull Generator<@Nullable JsonValue> slice(@NotNull Context context, @NotNull String type, int length, @NotNull BiFunction<Integer, Integer, JsonValue> slice) {
|
||||
return getIndices(start, context, type, length, 0)
|
||||
.mapToObj(start -> getIndices(end, context, type, length, length)
|
||||
.mapToObj(end -> start > end ? slice.apply(0, 0) : slice.apply(start, end))
|
||||
.map(start -> getIndices(end, context, type, length, length)
|
||||
.map(end -> start > end ? slice.apply(0, 0) : slice.apply(start, end))
|
||||
)
|
||||
.flatMap(Function.identity());
|
||||
}
|
||||
|
||||
private @NotNull IntStream getIndices(@Nullable JQExpression expression, @NotNull Context context, @NotNull String type, int length, int fallback) {
|
||||
if (expression == null) return IntStream.of(fallback);
|
||||
return expression.evaluate(context).mapToInt(value -> getIndex(value, type, length));
|
||||
private @NotNull Generator<Integer> getIndices(@Nullable JQExpression expression, @NotNull Context context, @NotNull String type, int length, int fallback) {
|
||||
if (expression == null) return Generator.of(fallback);
|
||||
return expression.evaluate(context).map(value -> getIndex(value, type, length));
|
||||
}
|
||||
|
||||
private int getIndex(@Nullable JsonValue value, @NotNull String type, int length) {
|
||||
|
@ -2,6 +2,7 @@ package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonString;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import eu.jonahbauer.json.query.util.Util;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -22,7 +23,7 @@ public record JQStringInterpolation(
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Util.crossReversed(values, context)
|
||||
.map(values -> StringTemplate.of(fragments, values.stream().map(JQStringInterpolation::toString).toList()))
|
||||
.map(STR::process)
|
||||
|
@ -4,13 +4,12 @@ import eu.jonahbauer.json.JsonString;
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.JsonQueryException;
|
||||
import eu.jonahbauer.json.query.JsonQueryHaltException;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public record JQTryExpression(@NotNull JQExpression expression, @Nullable JQExpression fallback) implements JQExpression {
|
||||
public JQTryExpression {
|
||||
@ -22,13 +21,11 @@ public record JQTryExpression(@NotNull JQExpression expression, @Nullable JQExpr
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
var iterator = new QuietIterator(
|
||||
expression.evaluate(context).iterator(),
|
||||
fallback == null ? null : ex -> fallback.evaluate(context.withRoot(new JsonString(ex.getMessage()))).iterator()
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return new QuietIterator(
|
||||
expression.evaluate(context),
|
||||
fallback == null ? null : ex -> fallback.evaluate(context.withRoot(new JsonString(ex.getMessage())))
|
||||
);
|
||||
var spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED);
|
||||
return StreamSupport.stream(spliterator, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -41,58 +38,62 @@ public record JQTryExpression(@NotNull JQExpression expression, @Nullable JQExpr
|
||||
return "try " + expression + (fallback == null ? "" : " catch " + fallback);
|
||||
}
|
||||
|
||||
private static class QuietIterator implements Iterator<@Nullable JsonValue> {
|
||||
private @Nullable Iterator<@Nullable JsonValue> delegate;
|
||||
private @Nullable Function<@NotNull JsonQueryException, @NotNull Iterator<@Nullable JsonValue>> fallback;
|
||||
private static class QuietIterator implements Generator<@Nullable JsonValue> {
|
||||
private @Nullable Generator<@Nullable JsonValue> delegate;
|
||||
private @Nullable Generator<@Nullable JsonValue> fallback;
|
||||
private @Nullable Function<@NotNull JsonQueryException, @NotNull Generator<@Nullable JsonValue>> fallbackSupplier;
|
||||
|
||||
private @Nullable JsonValue next;
|
||||
private boolean hasNext;
|
||||
|
||||
private boolean valid = false;
|
||||
private JsonValue value;
|
||||
private boolean hasValue;
|
||||
|
||||
private QuietIterator(
|
||||
@NotNull Iterator<@Nullable JsonValue> delegate,
|
||||
@Nullable Function<@NotNull JsonQueryException, @NotNull Iterator<@Nullable JsonValue>> fallback
|
||||
@NotNull Generator<@Nullable JsonValue> delegate,
|
||||
@Nullable Function<@NotNull JsonQueryException, @NotNull Generator<@Nullable JsonValue>> fallback
|
||||
) {
|
||||
this.delegate = delegate;
|
||||
this.fallback = fallback;
|
||||
this.fallbackSupplier = fallback;
|
||||
}
|
||||
|
||||
private void ensureValid() {
|
||||
if (valid) return;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
if (delegate != null && delegate.hasNext()) { // still have values in current stream
|
||||
next = delegate.next();
|
||||
hasNext = true;
|
||||
} else { // end of stream
|
||||
next = null;
|
||||
hasNext = false;
|
||||
}
|
||||
break;
|
||||
} catch (JsonQueryHaltException ex) {
|
||||
throw ex;
|
||||
} catch (JsonQueryException ex) { // switch to fallback
|
||||
delegate = fallback != null ? fallback.apply(ex) : null;
|
||||
fallback = null;
|
||||
private boolean advance() {
|
||||
if (hasValue) return true;
|
||||
assert fallback == null && delegate != null;
|
||||
try {
|
||||
hasValue = delegate.hasNext();
|
||||
value = hasValue ? delegate.next() : null;
|
||||
return hasValue;
|
||||
} catch (JsonQueryHaltException ex) {
|
||||
throw ex;
|
||||
} catch (JsonQueryException ex) {
|
||||
delegate = null;
|
||||
if (fallbackSupplier != null) {
|
||||
fallback = fallbackSupplier.apply(ex);
|
||||
hasValue = false;
|
||||
value = null;
|
||||
return fallback.hasNext();
|
||||
} else {
|
||||
hasValue = false;
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
ensureValid();
|
||||
return hasNext;
|
||||
if (fallback != null) return fallback.hasNext();
|
||||
if (delegate == null) return false;
|
||||
return advance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JsonValue next() {
|
||||
ensureValid();
|
||||
if (!hasNext) throw new NoSuchElementException();
|
||||
valid = false;
|
||||
return next;
|
||||
if (fallback != null) return fallback.next();
|
||||
if (delegate == null) throw new NoSuchElementException();
|
||||
if (!advance()) throw new NoSuchElementException();
|
||||
var out = value;
|
||||
hasValue = false;
|
||||
value = null;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package eu.jonahbauer.json.query.parser.ast;
|
||||
|
||||
import eu.jonahbauer.json.JsonValue;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public record JQVariableExpression(@NotNull String name) implements JQExpression {
|
||||
public JQVariableExpression {
|
||||
@ -14,8 +14,8 @@ public record JQVariableExpression(@NotNull String name) implements JQExpression
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Stream.of(context.variable(name));
|
||||
public @NotNull Generator<@Nullable JsonValue> evaluate(@NotNull Context context) {
|
||||
return Generator.of(() -> context.variable(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.jonahbauer.json.query.util;
|
||||
|
||||
import eu.jonahbauer.json.*;
|
||||
import eu.jonahbauer.json.query.impl.Generator;
|
||||
import eu.jonahbauer.json.query.parser.ast.JQExpression;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -15,42 +16,34 @@ import java.util.stream.Stream;
|
||||
@UtilityClass
|
||||
public class Util {
|
||||
|
||||
public static <T> @NotNull Stream<T> lazy(@NotNull Supplier<T> supplier) {
|
||||
return Stream.of((Object) null).map(_ -> supplier.get());
|
||||
}
|
||||
|
||||
public static <T> @NotNull Stream<T> lazyStream(@NotNull Supplier<Stream<T>> supplier) {
|
||||
return Stream.of((Object) null).flatMap(_ -> supplier.get());
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull List<JsonValue>> cross(@NotNull List<@NotNull JQExpression> expressions, @NotNull JQExpression.Context context) {
|
||||
public static @NotNull Generator<@NotNull List<JsonValue>> cross(@NotNull List<@NotNull JQExpression> expressions, @NotNull JQExpression.Context context) {
|
||||
return cross(asSupplier(expressions, context)).map(Collections::unmodifiableList);
|
||||
}
|
||||
|
||||
public static <T> @NotNull Stream<@NotNull List<T>> cross(@NotNull List<@NotNull Supplier<@NotNull Stream<T>>> expressions) {
|
||||
if (expressions.isEmpty()) return Stream.of(new ArrayList<T>().reversed());
|
||||
public static <T> @NotNull Generator<@NotNull List<T>> cross(@NotNull List<@NotNull Supplier<@NotNull Generator<T>>> expressions) {
|
||||
if (expressions.isEmpty()) return Generator.of(new ArrayList<T>().reversed());
|
||||
|
||||
return expressions.getFirst().get()
|
||||
.flatMap(value -> cross(expressions.subList(1, expressions.size()))
|
||||
.peek(list -> list.addFirst(value))
|
||||
.map(list -> { list.addFirst(value); return list; })
|
||||
);
|
||||
}
|
||||
|
||||
public static @NotNull Stream<@NotNull List<JsonValue>> crossReversed(@NotNull List<@NotNull JQExpression> expressions, @NotNull JQExpression.Context context) {
|
||||
public static @NotNull Generator<@NotNull List<JsonValue>> crossReversed(@NotNull List<@NotNull JQExpression> expressions, @NotNull JQExpression.Context context) {
|
||||
return crossReversed(asSupplier(expressions, context)).map(Collections::unmodifiableList);
|
||||
}
|
||||
|
||||
public static <T> @NotNull Stream<@NotNull List<T>> crossReversed(@NotNull List<@NotNull Supplier<@NotNull Stream<T>>> expressions) {
|
||||
if (expressions.isEmpty()) return Stream.of(new ArrayList<>());
|
||||
public static <T> @NotNull Generator<@NotNull List<T>> crossReversed(@NotNull List<@NotNull Supplier<@NotNull Generator<T>>> expressions) {
|
||||
if (expressions.isEmpty()) return Generator.of(new ArrayList<>());
|
||||
|
||||
return expressions.getLast().get()
|
||||
.flatMap(value -> crossReversed(expressions.subList(0, expressions.size() - 1))
|
||||
.peek(list -> list.addLast(value))
|
||||
.map(list -> { list.addLast(value); return list; })
|
||||
);
|
||||
}
|
||||
|
||||
private static @NotNull List<@NotNull Supplier<@NotNull Stream<JsonValue>>> asSupplier(@NotNull List<@NotNull JQExpression> expressions, @NotNull JQExpression.Context context) {
|
||||
var list = new ArrayList<Supplier<Stream<JsonValue>>>(expressions.size());
|
||||
private static @NotNull List<@NotNull Supplier<@NotNull Generator<JsonValue>>> asSupplier(@NotNull List<@NotNull JQExpression> expressions, @NotNull JQExpression.Context context) {
|
||||
var list = new ArrayList<Supplier<Generator<JsonValue>>>(expressions.size());
|
||||
expressions.forEach(expr -> list.add(() -> expr.evaluate(context)));
|
||||
return list;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user