All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.scravy.bedrock.CharParser Maven / Gradle / Ivy

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... 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 p : parsers) {
        final CharParser.Result 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);
  }
}