de.scravy.bedrock.CharParser 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 javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@FunctionalInterface
public interface CharParser {
CharParser.Result parse(final Seq seq);
default CharParser.Result parse(@Nonnull final String string) {
Objects.requireNonNull(string, "'string' must not be null");
return parse(Seq.wrap(string));
}
default CharParser map(@Nonnull final Function function) {
Objects.requireNonNull(function, "'function' must not be null");
return seq -> parse(seq).map(function);
}
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
abstract class Result {
@Nonnull
public abstract CharParser.Result map(@Nonnull final Function f);
@Nonnull
public abstract Seq getRemaining();
@Nonnull
public abstract CharParser.Result withRemaining(@Nonnull final Seq seq);
public E getValue() {
return null;
}
public boolean isSuccess() {
return false;
}
public boolean isNoParse() {
return false;
}
@SuppressWarnings("unchecked")
@Nonnull
CharParser.Result as() {
return (CharParser.Result) this;
}
@Value
@EqualsAndHashCode(callSuper = false)
public static class Success extends CharParser.Result {
private final E value;
@With
private final Seq remaining;
@Override
@Nonnull
public CharParser.Result map(@Nonnull 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 CharParser.Result {
@With
private final Seq remaining;
@SuppressWarnings("unchecked")
@Override
@Nonnull
public CharParser.Result map(@Nonnull final Function f) {
return (CharParser.Result) this;
}
@Override
public boolean isNoParse() {
return true;
}
}
}
/**
* Creates a parser that parses leftParser and then rightParser but returns the result of leftParser (the left one).
*
* @param leftParser The left parser.
* @param rightParser The right parser.
* @param The type parsed by the left parser.
* @param The type parsed by the right parser.
* @return A parser that parses leftParser and then rightParser but returns the result of leftParser (the left one).
*/
@Nonnull
static CharParser left(
@Nonnull final CharParser leftParser,
@Nonnull final CharParser rightParser
) {
Objects.requireNonNull(leftParser, "'leftParser' must not be null.");
Objects.requireNonNull(rightParser, "'rightParser' must not be null.");
return seq -> {
final CharParser.Result r1 = leftParser.parse(seq);
if (r1.isSuccess()) {
final CharParser.Result r2 = rightParser.parse(r1.getRemaining());
if (r2.isSuccess()) {
return new CharParser.Result.Success<>(r1.getValue(), r2.getRemaining());
}
return r2.withRemaining(seq).as();
}
return r1.withRemaining(seq);
};
}
/**
* Creates a parser that parses leftParser and then rightParser but returns the result of rightParser (the right one).
*
* @param leftParser The left parser.
* @param rightParser The right parser.
* @param The type parsed by the left parser.
* @param The type parsed by the right parser.
* @return A parser that parses leftParser and then rightParser but returns the result of rightParser (the right one).
*/
@Nonnull
static CharParser right(
@Nonnull final CharParser leftParser,
@Nonnull final CharParser rightParser
) {
Objects.requireNonNull(leftParser, "'leftParser' must not be null.");
Objects.requireNonNull(rightParser, "'rightParser' must not be null.");
return seq -> {
final CharParser.Result r1 = leftParser.parse(seq);
if (r1.isSuccess()) {
final CharParser.Result r2 = rightParser.parse(r1.getRemaining());
if (r2.isSuccess()) {
return r2;
}
return r2.withRemaining(seq);
}
return r1.withRemaining(seq).as();
};
}
@Nonnull
static CharParser choice(
@Nonnull final CharParser leftParser,
@Nonnull final CharParser rightParser
) {
Objects.requireNonNull(leftParser, "'leftParser' must not be null.");
Objects.requireNonNull(rightParser, "'rightParser' must not be null.");
return seq -> {
final CharParser.Result r1 = leftParser.parse(seq);
if (r1.isSuccess()) {
return r1.as();
}
return rightParser.parse(seq).as();
};
}
@Nonnull
static CharParser> either(
@Nonnull final CharParser leftParser,
@Nonnull final CharParser rightParser
) {
Objects.requireNonNull(leftParser, "'leftParser' must not be null.");
Objects.requireNonNull(rightParser, "'rightParser' must not be null.");
return seq -> {
final CharParser.Result> r1 = leftParser.parse(seq).map(Either::left);
if (r1.isSuccess()) {
return r1;
}
return rightParser.parse(seq).map(Either::right);
};
}
@Nonnull
@SafeVarargs
static CharParser oneOf(@Nonnull final CharParser extends T>... parsers) {
Objects.requireNonNull(parsers, "'parsers' must not be null.");
if (parsers.length == 1) {
@SuppressWarnings("unchecked") final CharParser parser = (CharParser) parsers[0];
return parser;
}
return seq -> {
for (final CharParser extends T> p : parsers) {
final CharParser.Result extends T> result = p.parse(seq);
if (result.isSuccess()) {
return result.as();
}
}
return new CharParser.Result.NoParse<>(seq);
};
}
@Nonnull
static CharParser anyOf(@Nonnull final String string) {
Objects.requireNonNull(string, "'string' must not be null.");
if (string.isEmpty()) {
return Result.NoParse::new;
}
if (string.length() == 1) {
return character(string.charAt(0));
}
final char[] chars = string.toCharArray();
Arrays.sort(chars);
char c = chars[0];
int i = 1;
while (i < chars.length) {
if (c + 1 != chars[i]) {
break;
}
c = chars[i++];
}
if (i == chars.length) { // loop did not exit before exit condition
return range(chars[0], chars[chars.length - 1]);
}
return seq -> {
if (seq.nonEmpty()) {
final char chr = seq.head();
if (Arrays.binarySearch(chars, chr) >= 0) {
return new CharParser.Result.Success<>(chr, seq.tailView());
}
}
return new Result.NoParse<>(seq);
};
}
@Nonnull
static CharParser noneOf(@Nonnull final String string) {
Objects.requireNonNull(string, "'string' must not be null.");
return seq -> {
if (seq.nonEmpty()) {
final char c = seq.head();
if (string.indexOf(c) < 0) {
return new CharParser.Result.Success<>(c, seq.tailView());
}
}
return new Result.NoParse<>(seq);
};
}
@Nonnull
static CharParser character(final char character) {
return seq -> {
if (seq.nonEmpty()) {
final Character c = seq.head();
if (c == character) {
return new CharParser.Result.Success<>(c, seq.tailView());
}
}
return new CharParser.Result.NoParse<>(seq);
};
}
@Nonnull
static CharParser range(final char lower, final char upper) {
return seq -> {
if (seq.nonEmpty()) {
final Character c = seq.head();
if (c >= lower && c <= upper) {
return new CharParser.Result.Success<>(c, seq.tailView());
}
}
return new CharParser.Result.NoParse<>(seq);
};
}
@Nonnull
static CharParser string(@Nonnull final String string) {
Objects.requireNonNull(string, "'string' must not be null.");
return seq -> {
if (seq.length() < string.length()) {
return new CharParser.Result.NoParse<>(seq);
}
for (int i = 0; i < string.length(); i += 1) {
if (seq.get(i) != string.charAt(i)) {
return new CharParser.Result.NoParse<>(seq);
}
}
return new CharParser.Result.Success<>(string, seq.dropView(string.length()));
};
}
@Nonnull
static CharParser satisfies(@Nonnull final Predicate predicate) {
Objects.requireNonNull(predicate, "'predicate' must not be null.");
return seq -> {
if (seq.nonEmpty()) {
final Character c = seq.head();
if (predicate.test(c)) {
return new CharParser.Result.Success<>(c, seq.tailView());
}
}
return new CharParser.Result.NoParse<>(seq);
};
}
@Nonnull
static CharParser satisfies2(@Nonnull final Function> function) {
Objects.requireNonNull(function, "'function' must not be null.");
return seq -> {
if (seq.nonEmpty()) {
final Character c = seq.head();
final Optional result = function.apply(c);
if (result.isPresent()) {
return new CharParser.Result.Success<>(result.get(), seq.tailView());
}
}
return new CharParser.Result.NoParse<>(seq);
};
}
@Nonnull
static CharParser> optional(@Nonnull final CharParser parser) {
Objects.requireNonNull(parser, "'parser' must not be null.");
return seq -> {
final CharParser.Result result = parser.parse(seq);
if (result.isNoParse()) {
return new CharParser.Result.Success<>(Optional.empty(), result.getRemaining());
}
return result.map(Optional::of);
};
}
@Nonnull
static CharParser option(
final T fallback,
@Nonnull final CharParser parser
) {
Objects.requireNonNull(parser, "'parser' must not be null.");
return optional(parser).map(optional -> optional.orElse(fallback));
}
@Nonnull
static CharParser optionOrGet(
@Nonnull final Supplier fallbackSupplier,
@Nonnull final CharParser parser
) {
Objects.requireNonNull(fallbackSupplier, "'fallbackSupplier' must not be null.");
Objects.requireNonNull(parser, "'parser' must not be null.");
return optional(parser).map(optional -> optional.orElseGet(fallbackSupplier));
}
@Nonnull
@SafeVarargs
static CharParser> sequence(@Nonnull final CharParser... parsers) {
Objects.requireNonNull(parsers, "'parsers' must not be null");
if (parsers.length == 1) {
return parsers[0].map(Seq::of);
}
final Seq> s = Seq.wrap(parsers);
return sequence(s);
}
@Nonnull
static CharParser> sequence(@Nonnull final Iterable> parsers) {
Objects.requireNonNull(parsers, "'parsers' must not be null");
if (parsers instanceof Collection && ((Collection>) parsers).size() == 1
|| (parsers instanceof HasLength && ((HasLength) parsers).length() == 1)) {
return parsers.iterator().next().map(Seq::of);
}
return seq -> {
final SeqBuilder seqBuilder = Seq.builder();
Seq remaining = seq;
CharParser.Result result;
for (final CharParser p : parsers) {
if (remaining.isEmpty()) {
return new CharParser.Result.NoParse<>(seq);
}
result = p.parse(remaining);
if (!result.isSuccess()) {
return result.withRemaining(seq).as();
}
remaining = result.getRemaining();
seqBuilder.add(result.getValue());
}
return new CharParser.Result.Success<>(seqBuilder.result(), remaining);
};
}
@Nonnull
static CharParser> times(@Nonnegative final int n, @Nonnull final CharParser parser) {
Objects.requireNonNull(parser, "'parser' must not be null.");
return seq -> {
final SeqBuilder seqBuilder = Seq.builder();
Seq remaining = seq;
CharParser.Result result;
for (int i = 0; i < n; i += 1) {
if (remaining.isEmpty()) {
return new CharParser.Result.NoParse<>(seq);
}
result = parser.parse(remaining);
if (!result.isSuccess()) {
return result.withRemaining(seq).as();
}
remaining = result.getRemaining();
seqBuilder.add(result.getValue());
}
return new CharParser.Result.Success<>(seqBuilder.result(), remaining);
};
}
@Nonnull
static CharParser> seq(
@Nonnull final CharParser p1,
@Nonnull final CharParser p2
) {
Objects.requireNonNull(p1, "'p1' must not be null.");
Objects.requireNonNull(p2, "'p2' must not be null.");
return seq -> {
final CharParser.Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
final CharParser.Result r2 = p2.parse(r1.getRemaining());
if (r2.isSuccess()) {
return new CharParser.Result.Success<>(Pair.of(r1.getValue(), r2.getValue()), r2.getRemaining());
}
return r2.withRemaining(seq).as();
}
return r1.withRemaining(seq).as();
};
}
@Nonnull
static CharParser> seq(
@Nonnull final CharParser p1,
@Nonnull final CharParser p2,
@Nonnull final CharParser p3
) {
Objects.requireNonNull(p1, "'p1' must not be null.");
Objects.requireNonNull(p2, "'p2' must not be null.");
Objects.requireNonNull(p3, "'p3' must not be null.");
return seq -> {
final CharParser.Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
final CharParser.Result r2 = p2.parse(r1.getRemaining());
if (r2.isSuccess()) {
final CharParser.Result r3 = p3.parse(r2.getRemaining());
if (r3.isSuccess()) {
return new CharParser.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();
};
}
@Nonnull
static CharParser> seq(
@Nonnull final CharParser p1,
@Nonnull final CharParser p2,
@Nonnull final CharParser p3,
@Nonnull final CharParser p4
) {
Objects.requireNonNull(p1, "'p1' must not be null.");
Objects.requireNonNull(p2, "'p2' must not be null.");
Objects.requireNonNull(p3, "'p3' must not be null.");
Objects.requireNonNull(p4, "'p4' must not be null.");
return seq -> {
final CharParser.Result r1 = p1.parse(seq);
if (r1.isSuccess()) {
final CharParser.Result r2 = p2.parse(r1.getRemaining());
if (r2.isSuccess()) {
final CharParser.Result r3 = p3.parse(r2.getRemaining());
if (r3.isSuccess()) {
final CharParser.Result r4 = p4.parse(r3.getRemaining());
if (r4.isSuccess()) {
return new CharParser.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();
};
}
@Nonnull
static CharParser> many(@Nonnull final CharParser parser) {
Objects.requireNonNull(parser, "'parser' must not be null.");
return seq -> {
final SeqBuilder resultBuilder = Seq.builder();
Seq remaining = seq;
while (remaining.nonEmpty()) {
final CharParser.Result result = parser.parse(remaining);
if (!result.isSuccess()) {
return new CharParser.Result.Success<>(resultBuilder.result(), remaining);
}
resultBuilder.add(result.getValue());
remaining = result.getRemaining();
}
return new CharParser.Result.Success<>(resultBuilder.result(), remaining);
};
}
@Nonnull
static CharParser> many1(@Nonnull final CharParser parser) {
Objects.requireNonNull(parser, "'parser' must not be null.");
final CharParser> manyCharParser = many(parser);
return seq -> {
final CharParser.Result> result = manyCharParser.parse(seq);
if (result.isSuccess() && result.getValue().isEmpty()) {
return new CharParser.Result.NoParse<>(seq);
}
return result;
};
}
@Nonnull
static CharParser skipMany(@Nonnull final CharParser parser) {
Objects.requireNonNull(parser, "'parser' must not be null.");
return seq -> {
Seq remaining = seq;
while (remaining.nonEmpty()) {
final CharParser.Result result = parser.parse(remaining);
if (!result.isSuccess()) {
return new CharParser.Result.Success<>(null, remaining);
}
remaining = result.getRemaining();
}
return new CharParser.Result.Success<>(null, remaining);
};
}
@Nonnull
static CharParser> sepBy(
@Nonnull final CharParser parser,
@Nonnull final CharParser sep
) {
Objects.requireNonNull(parser, "'parser' must not be null.");
Objects.requireNonNull(sep, "'sep' must not be null.");
final CharParser p = right(sep, parser);
return seq -> {
final SeqBuilder resultBuilder = Seq.builder();
Seq remaining = seq;
CharParser.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 CharParser.Result.Success<>(resultBuilder.result(), remaining);
}
resultBuilder.add(result.getValue());
remaining = result.getRemaining();
}
}
return new CharParser.Result.Success<>(resultBuilder.result(), remaining);
};
}
@Nonnull
static CharParser> sepBy1(
@Nonnull final CharParser parser,
@Nonnull final CharParser sep
) {
Objects.requireNonNull(parser, "'parser' must not be null.");
Objects.requireNonNull(sep, "'sep' must not be null.");
final CharParser> sepByCharParser = sepBy(parser, sep);
return seq -> {
final CharParser.Result> result = sepByCharParser.parse(seq);
if (result.isSuccess() && result.getValue().isEmpty()) {
return new CharParser.Result.NoParse<>(seq);
}
return result;
};
}
@Nonnull
static CharParser between(
@Nonnull final CharParser s,
@Nonnull final CharParser t,
@Nonnull final CharParser u
) {
Objects.requireNonNull(s, "'s' must not be null.");
Objects.requireNonNull(t, "'t' must not be null.");
Objects.requireNonNull(u, "'u' must not be null.");
return seq(s, t, u).map(Triple::getSecond);
}
/**
* Creates a parser that is constructed at first invocation for recursive invocation
* of the parser.
*/
@Nonnull
static CharParser recursive(@Nonnull final Supplier> supplier) {
Objects.requireNonNull(supplier, "'supplier' must not be null.");
final Supplier> parserSupplier = Control.memoizing(supplier);
return seq -> parserSupplier.get().parse(seq);
}
}