de.scravy.bedrock.Parser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bedrock Show documentation
Show all versions of bedrock Show documentation
Essential utilities for modern Java.
The newest version!
package de.scravy.bedrock;
import lombok.*;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Optional;
import java.util.function.*;
@FunctionalInterface
public interface Parser {
Result parse(final Seq> seq);
default Parser map(final Function f) {
return seq -> parse(seq).map(f);
}
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
abstract class Result {
public abstract Result map(final Function f);
public abstract Seq> getRemaining();
public abstract Result withRemaining(final Seq> seq);
public E getValue() {
return null;
}
public boolean isSuccess() {
return false;
}
public boolean isNoParse() {
return false;
}
@SuppressWarnings("unchecked")
Result as() {
return (Result) this;
}
@Value
@EqualsAndHashCode(callSuper = false)
public static class Success extends Result {
private final E value;
@With
private final Seq> remaining;
@Override
public Result map(final Function f) {
return new Success<>(f.apply(value), remaining);
}
@Override
public boolean isSuccess() {
return true;
}
}
@Value
@EqualsAndHashCode(callSuper = false)
public static class NoParse extends Result {
@With
private final Seq> remaining;
@SuppressWarnings("unchecked")
@Override
public Result map(final Function f) {
return (Result) this;
}
@Override
public boolean isNoParse() {
return true;
}
}
}
/**
* Creates a parser that parses p1 and then p2 but returns the result of p1 (the left one).
*
* @param p1 The left parser.
* @param p2 The right parser.
* @param The type parsed by the left parser.
* @param The type parsed by the right parser.
* @return A parser that parses p1 and then p2 but returns the result of p1 (the left one).
*/
static Parser left(final Parser p1, final Parser p2) {
return seq -> {
final Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
final Result r2 = p2.parse(r1.getRemaining());
if (r2.isSuccess()) {
return new Result.Success<>(r1.getValue(), r2.getRemaining());
}
return r2.withRemaining(seq).as();
}
return r1.withRemaining(seq);
};
}
/**
* Creates a parser that parses p1 and then p2 but returns the result of p2 (the right one).
*
* @param p1 The left parser.
* @param p2 The right parser.
* @param The type parsed by the left parser.
* @param The type parsed by the right parser.
* @return A parser that parses p1 and then p2 but returns the result of p2 (the right one).
*/
static Parser right(final Parser p1, final Parser p2) {
return seq -> {
final Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
final Result r2 = p2.parse(r1.getRemaining());
if (r2.isSuccess()) {
return r2;
}
return r2.withRemaining(seq);
}
return r1.withRemaining(seq).as();
};
}
static Parser choice(final Parser p1, final Parser p2) {
return seq -> {
final Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
return r1.as();
}
return p2.parse(seq).as();
};
}
static Parser> either(final Parser p1, final Parser p2) {
return seq -> {
final Result> r1 = p1.parse(seq).map(Either::left);
if (r1.isSuccess()) {
return r1;
}
return p2.parse(seq).map(Either::right);
};
}
@SafeVarargs
static Parser oneOf(final Parser extends T>... ps) {
return seq -> {
for (final Parser extends T> p : ps) {
final Result extends T> result = p.parse(seq);
if (result.isSuccess()) {
return result.as();
}
}
return new Result.NoParse<>(seq);
};
}
static Parser satisfies(final Class clazz, final Predicate predicate) {
return seq -> {
if (seq.nonEmpty()) {
final Object obj = seq.head();
if (clazz.isAssignableFrom(obj.getClass())) {
@SuppressWarnings("unchecked") final T t = (T) obj;
if (predicate.test(t)) {
return new Result.Success<>(t, seq.tailView());
}
}
}
return new Result.NoParse<>(seq);
};
}
static Parser satisfies2(final Class clazz, final Function> f) {
return seq -> {
if (seq.nonEmpty()) {
final Object obj = seq.head();
if (clazz.isAssignableFrom(obj.getClass())) {
@SuppressWarnings("unchecked") final T t = (T) obj;
final Optional result = f.apply(t);
if (result.isPresent()) {
return new Result.Success<>(result.get(), seq.tailView());
}
}
}
return new Result.NoParse<>(seq);
};
}
static Parser recurse(
final Class clazz,
final Predicate predicate,
final Function> extractor,
final Parser parser) {
return seq -> {
if (seq.nonEmpty()) {
final Object obj = seq.head();
if (clazz.isAssignableFrom(obj.getClass())) {
@SuppressWarnings("unchecked") final T t = (T) obj;
if (predicate.test(t)) {
final Seq> rseq = extractor.apply(t);
final Result result = parser.parse(rseq);
if (result.isSuccess()) {
if (result.getRemaining().nonEmpty()) {
return new Result.NoParse<>(seq);
}
return result.withRemaining(seq.tailView());
}
return result.withRemaining(seq);
}
}
}
return new Result.NoParse<>(seq);
};
}
static Parser recurse2(
final Class clazz,
final Function> extractor,
final Function> parser) {
return seq -> {
if (seq.nonEmpty()) {
final Object obj = seq.head();
if (clazz.isAssignableFrom(obj.getClass())) {
@SuppressWarnings("unchecked") final T t = (T) obj;
final Seq> rseq = extractor.apply(t);
final Result result = parser.apply(t).parse(rseq);
if (result.isSuccess()) {
if (result.getRemaining().nonEmpty()) {
return new Result.NoParse<>(seq);
}
return result.withRemaining(seq.tailView());
}
return result.withRemaining(seq);
}
}
return new Result.NoParse<>(seq);
};
}
static Parser> optional(final Parser parser) {
return seq -> {
final Result result = parser.parse(seq);
if (result.isNoParse()) {
return new Result.Success<>(Optional.empty(), result.getRemaining());
}
return result.map(Optional::of);
};
}
static Parser option(final T fallback, final Parser parser) {
return optional(parser).map(optional -> optional.orElse(fallback));
}
static Parser option(final Supplier fallbackSupplier, final Parser parser) {
return optional(parser).map(optional -> optional.orElseGet(fallbackSupplier));
}
@SafeVarargs
static Parser> sequence(final Parser... ps) {
return seq -> {
final SeqBuilder seqBuilder = Seq.builder();
Seq> remaining = seq;
Result result;
for (final Parser p : ps) {
if (remaining.isEmpty()) {
return new Result.NoParse<>(seq);
}
result = p.parse(remaining);
if (!result.isSuccess()) {
return result.withRemaining(seq).as();
}
remaining = result.getRemaining();
seqBuilder.add(result.getValue());
}
return new Result.Success<>(seqBuilder.result(), remaining);
};
}
static Parser> sequence(final Iterable> ps) {
return seq -> {
final SeqBuilder seqBuilder = Seq.builder();
Seq> remaining = seq;
Result result;
for (final Parser p : ps) {
if (remaining.isEmpty()) {
return new Result.NoParse<>(seq);
}
result = p.parse(remaining);
if (!result.isSuccess()) {
return result.withRemaining(seq).as();
}
remaining = result.getRemaining();
seqBuilder.add(result.getValue());
}
return new Result.Success<>(seqBuilder.result(), remaining);
};
}
static Parser> times(final int n, final Parser p) {
return seq -> {
final SeqBuilder seqBuilder = Seq.builder();
Seq> remaining = seq;
Result result;
for (int i = 0; i < n; i += 1) {
if (remaining.isEmpty()) {
return new Result.NoParse<>(seq);
}
result = p.parse(remaining);
if (!result.isSuccess()) {
return result.withRemaining(seq).as();
}
remaining = result.getRemaining();
seqBuilder.add(result.getValue());
}
return new Result.Success<>(seqBuilder.result(), remaining);
};
}
static Parser> seq(
final Parser p1,
final Parser p2) {
return seq -> {
final Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
final Result r2 = p2.parse(r1.getRemaining());
if (r2.isSuccess()) {
return new Result.Success<>(Pair.of(r1.getValue(), r2.getValue()), r2.getRemaining());
}
return r2.withRemaining(seq).as();
}
return r1.withRemaining(seq).as();
};
}
static Parser> seq(
final Parser p1,
final Parser p2,
final Parser p3) {
return seq -> {
final Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
final Result r2 = p2.parse(r1.getRemaining());
if (r2.isSuccess()) {
final Result r3 = p3.parse(r2.getRemaining());
if (r3.isSuccess()) {
return new Result.Success<>(
Triple.of(r1.getValue(), r2.getValue(), r3.getValue()), r3.getRemaining()
);
}
return r3.withRemaining(seq).as();
}
return r2.withRemaining(seq).as();
}
return r1.withRemaining(seq).as();
};
}
static Parser> seq(
final Parser p1,
final Parser p2,
final Parser p3,
final Parser p4) {
return seq -> {
final Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
final Result r2 = p2.parse(r1.getRemaining());
if (r2.isSuccess()) {
final Result r3 = p3.parse(r2.getRemaining());
if (r3.isSuccess()) {
final Result r4 = p4.parse(r3.getRemaining());
if (r4.isSuccess()) {
return new Result.Success<>(
Quadruple.of(r1.getValue(), r2.getValue(), r3.getValue(), r4.getValue()), r4.getRemaining()
);
}
return r4.withRemaining(seq).as();
}
return r3.withRemaining(seq).as();
}
return r2.withRemaining(seq).as();
}
return r1.withRemaining(seq).as();
};
}
static Parser> many(final Parser parser) {
return seq -> {
final SeqBuilder resultBuilder = Seq.builder();
Seq> remaining = seq;
while (remaining.nonEmpty()) {
final Result result = parser.parse(remaining);
if (!result.isSuccess()) {
return new Result.Success<>(resultBuilder.result(), remaining);
}
resultBuilder.add(result.getValue());
remaining = result.getRemaining();
}
return new Result.Success<>(resultBuilder.result(), remaining);
};
}
static Parser> many1(final Parser parser) {
final Parser> manyParser = many(parser);
return seq -> {
final Result> result = manyParser.parse(seq);
if (result.isSuccess() && result.getValue().isEmpty()) {
return new Result.NoParse<>(seq);
}
return result;
};
}
static Parser skipMany(final Parser parser) {
return seq -> {
Seq> remaining = seq;
while (remaining.nonEmpty()) {
final Result result = parser.parse(remaining);
if (!result.isSuccess()) {
return new Result.Success<>(null, remaining);
}
remaining = result.getRemaining();
}
return new Result.Success<>(null, remaining);
};
}
static Parser> sepBy(final Parser parser, final Parser sep) {
final Parser p = right(sep, parser);
return seq -> {
final SeqBuilder resultBuilder = Seq.builder();
Seq> remaining = seq;
Result result = parser.parse(remaining);
if (result.isSuccess()) {
resultBuilder.add(result.getValue());
remaining = result.getRemaining();
while (remaining.nonEmpty()) {
result = p.parse(remaining);
if (!result.isSuccess()) {
return new Result.Success<>(resultBuilder.result(), remaining);
}
resultBuilder.add(result.getValue());
remaining = result.getRemaining();
}
}
return new Result.Success<>(resultBuilder.result(), remaining);
};
}
static Parser> sepBy1(final Parser parser, final Parser sep) {
final Parser> sepByParser = sepBy(parser, sep);
return seq -> {
final Result> result = sepByParser.parse(seq);
if (result.isSuccess() && result.getValue().isEmpty()) {
return new Result.NoParse<>(seq);
}
return result;
};
}
/**
* Creates a parser that is constructed at first invocation for recursive invocation
* of the parser.
*/
static Parser recursive(final Supplier> supplier) {
final Supplier> parserSupplier = Control.memoizing(supplier);
return seq -> parserSupplier.get().parse(seq);
}
static boolean shuntingYard(
final Iterable in,
final Consumer out,
final Mapping precedenceMap,
final Predicate isLeftAssociative,
final Predicate isLeftBracket,
final Predicate isRightBracket
) {
return shuntingYard(
in,
out,
precedenceMap.keys()::contains,
(a, b) -> precedenceMap.apply(a) > precedenceMap.apply(b),
(a, b) -> precedenceMap.apply(a).equals(precedenceMap.apply(b)),
isLeftAssociative,
isLeftBracket,
isRightBracket
);
}
static boolean shuntingYard(
final Iterable in,
final Consumer out,
final Predicate isOperator,
final BiPredicate hasHigherPrecedenceThan,
final BiPredicate hasEqualPrecedenceAs,
final Predicate isLeftAssociative,
final Predicate isLeftBracket,
final Predicate isRightBracket
) {
final Deque stack = new ArrayDeque<>();
for (final T s : in) {
if (isOperator.test(s)) {
while (!stack.isEmpty() && isOperator.test(stack.peek()) &&
(hasHigherPrecedenceThan.test(stack.peek(), s)
|| (hasEqualPrecedenceAs.test(stack.peek(), s) && isLeftAssociative.test(stack.peek())))) {
out.accept(stack.pop());
}
stack.push(s);
} else if (isLeftBracket.test(s)) {
stack.push(s);
} else if (isRightBracket.test(s)) {
while (!stack.isEmpty() && !isLeftBracket.test(stack.peek())) {
out.accept(stack.pop());
}
if (!stack.isEmpty() && isLeftBracket.test(stack.peek())) {
stack.pop();
} else {
return false;
}
} else {
out.accept(s);
}
}
while (!stack.isEmpty()) {
final T t = stack.pop();
if (!isOperator.test(t)) {
return false;
}
out.accept(t);
}
return true;
}
}