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