<|start_filename|>petitparser-core/src/main/java/org/petitparser/context/Context.java<|end_filename|>
package org.petitparser.context;

/**
 * An immutable parse context.
 */
public class Context {

  /**
   * The input buffer.
   */
  protected final String buffer;

  /**
   * The current position.
   */
  protected final int position;

  /**
   * Constructs an immutable parse context.
   *
   * @param buffer the buffer this context is using
   * @param position the position this context is pointing at
   */
  public Context(String buffer, int position) {
    this.buffer = buffer;
    this.position = position;
  }

  /**
   * Returns the input buffer.
   */
  public String getBuffer() {
    return buffer;
  }

  /**
   * Returns the current position.
   */
  public int getPosition() {
    return position;
  }

  /**
   * Returns a successful parse result at the current position.
   *
   * @param value the value of the parse result
   */
  public Success success(Object value) {
    return success(value, position);
  }

  /**
   * Returns a successful parse result.
   *
   * @param value the value of the parse result
   * @param position the position of the parse result
   */
  public Success success(Object value, int position) {
    return new Success(buffer, position, value);
  }

  /**
   * Returns a parse failure at the current position.
   *
   * @param message the error message of the parse result
   */
  public Failure failure(String message) {
    return failure(message, position);
  }

  /**
   * Returns a successful parse result.
   *
   * @param message the error message of the parse result
   * @param position the position of the parse result
   */
  public Failure failure(String message, int position) {
    return new Failure(buffer, position, message);
  }

  @Override
  public String toString() {
    int[] tuple = Token.lineAndColumnOf(buffer, position);
    return getClass().getSimpleName() + "[" + tuple[0] + ":" + tuple[1] + "]";
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/XmlBuilder.java<|end_filename|>
package org.petitparser.grammar.xml;

import org.petitparser.grammar.xml.ast.XmlAttribute;
import org.petitparser.grammar.xml.ast.XmlCdata;
import org.petitparser.grammar.xml.ast.XmlComment;
import org.petitparser.grammar.xml.ast.XmlDoctype;
import org.petitparser.grammar.xml.ast.XmlDocument;
import org.petitparser.grammar.xml.ast.XmlElement;
import org.petitparser.grammar.xml.ast.XmlName;
import org.petitparser.grammar.xml.ast.XmlNode;
import org.petitparser.grammar.xml.ast.XmlProcessing;
import org.petitparser.grammar.xml.ast.XmlText;

import java.util.Collection;

/**
 * Builds the XML AST.
 */
public class XmlBuilder implements XmlCallback<XmlName, XmlNode, XmlAttribute> {

  @Override
  public XmlNode createAttribute(XmlName name, String text) {
    return new XmlAttribute(name, text);
  }

  @Override
  public XmlNode createComment(String text) {
    return new XmlComment(text);
  }

  @Override
  public XmlNode createCDATA(String text) {
    return new XmlCdata(text);
  }

  @Override
  public XmlNode createDoctype(String text) {
    return new XmlDoctype(text);
  }

  @Override
  public XmlNode createDocument(Collection<XmlNode> children) {
    return new XmlDocument(children);
  }

  @Override
  public XmlNode createElement(
      XmlName name, Collection<XmlAttribute> attributes,
      Collection<XmlNode> children) {
    return new XmlElement(name, attributes, children);
  }

  @Override
  public XmlNode createProcessing(String target, String text) {
    return new XmlProcessing(target, text);
  }

  @Override
  public XmlName createQualified(String name) {
    return new XmlName(name);
  }

  @Override
  public XmlNode createText(String text) {
    return new XmlText(text);
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/primitive/EpsilonParser.java<|end_filename|>
package org.petitparser.parser.primitive;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

/**
 * A parser that consumes nothing and always succeeds.
 */
public class EpsilonParser extends Parser {

  @Override
  public Result parseOn(Context context) {
    return context.success(null);
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    return position;
  }

  @Override
  public EpsilonParser copy() {
    return new EpsilonParser();
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlAttribute.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import org.petitparser.grammar.xml.XmlCharacterParser;
import org.petitparser.grammar.xml.XmlDefinition;

import java.util.Objects;

/**
 * XML attribute node.
 */
public class XmlAttribute extends XmlNode {

  private final XmlName name;
  private final String value;

  public XmlAttribute(XmlName name, String value) {
    this.name = name;
    this.value = value;
  }

  public XmlName getName() {
    return name;
  }

  public String getValue() {
    return value;
  }

  @Override
  public void writeTo(StringBuilder buffer) {
    name.writeTo(buffer);
    buffer.append(XmlDefinition.EQUALS);
    buffer.append(XmlDefinition.DOUBLE_QUOTE);
    buffer.append(XmlCharacterParser.encodeXmlAttributeValue(value));
    buffer.append(XmlDefinition.DOUBLE_QUOTE);
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null || getClass() != obj.getClass()) {
      return false;
    }
    XmlAttribute other = (XmlAttribute) obj;
    return Objects.equals(name, other.name) &&
        Objects.equals(value, other.value);
  }

  @Override
  public int hashCode() {
    return Objects.hash(name, value);
  }
}


<|start_filename|>petitparser-core/src/test/java/org/petitparser/utils/FunctionsTest.java<|end_filename|>
package org.petitparser.utils;

import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.petitparser.utils.Functions.constant;
import static org.petitparser.utils.Functions.firstOfList;
import static org.petitparser.utils.Functions.lastOfList;
import static org.petitparser.utils.Functions.nthOfList;
import static org.petitparser.utils.Functions.permutationOfList;
import static org.petitparser.utils.Functions.withoutSeparators;

import org.junit.Test;

import java.util.List;

/**
 * Tests {@link Functions}.
 */
public class FunctionsTest {

  static final List<Object> EMPTY = asList();
  static final List<Object> ONE = asList('a');
  static final List<Object> TWO = asList('a', 'b');
  static final List<Object> THREE = asList('a', 'b', 'c');

  @Test
  public void testFirstOfList() {
    assertEquals('a', firstOfList().apply(ONE));
    assertEquals('a', firstOfList().apply(TWO));
    assertEquals('a', firstOfList().apply(THREE));
  }

  @Test
  public void testLastOfList() {
    assertEquals('a', lastOfList().apply(ONE));
    assertEquals('b', lastOfList().apply(TWO));
    assertEquals('c', lastOfList().apply(THREE));
  }

  @Test
  public void testNthOfListOfList() {
    assertEquals('a', nthOfList(0).apply(ONE));
    assertEquals('a', nthOfList(0).apply(TWO));
    assertEquals('a', nthOfList(0).apply(THREE));

    assertEquals('b', nthOfList(1).apply(TWO));
    assertEquals('b', nthOfList(1).apply(THREE));

    assertEquals('c', nthOfList(2).apply(THREE));

    assertEquals('a', nthOfList(-1).apply(ONE));
    assertEquals('b', nthOfList(-1).apply(TWO));
    assertEquals('c', nthOfList(-1).apply(THREE));

    assertEquals('a', nthOfList(-2).apply(TWO));
    assertEquals('b', nthOfList(-2).apply(THREE));

    assertEquals('a', nthOfList(-3).apply(THREE));
  }

  @Test
  public void testPermutationOfList() {
    assertEquals(EMPTY, permutationOfList().apply(EMPTY));
    assertEquals(EMPTY, permutationOfList().apply(THREE));
    assertEquals(asList('c', 'a'), permutationOfList(-1, 0).apply(THREE));
    assertEquals(asList('a', 'a'), permutationOfList(-3, 0).apply(THREE));
  }

  @Test
  public void testWithoutSeparators() {
    assertEquals(EMPTY, withoutSeparators().apply(EMPTY));
    assertEquals(ONE, withoutSeparators().apply(ONE));
    assertEquals(ONE, withoutSeparators().apply(TWO));
    assertEquals(asList('a', 'c'), withoutSeparators().apply(THREE));
  }

  @Test
  public void testConstant() {
    assertEquals((Object) 'a', constant('a').apply('b'));
    assertEquals((Object) 'b', constant('b').apply('c'));
  }
}


<|start_filename|>petitparser-smalltalk/src/main/java/org/petitparser/grammar/smalltalk/package-info.java<|end_filename|>
/**
 * This package contains the complete grammar of Smalltalk.
 */
package org.petitparser.grammar.smalltalk;


<|start_filename|>petitparser-core/src/main/java/org/petitparser/utils/Mirror.java<|end_filename|>
package org.petitparser.utils;

import org.petitparser.parser.Parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * A reflective parser mirror.
 */
public class Mirror implements Iterable<Parser> {

  /**
   * Constructs a mirror of the provided {@code parser}.
   */
  public static Mirror of(Parser parser) {
    return new Mirror(parser);
  }

  private final Parser parser;

  private Mirror(Parser parser) {
    this.parser = Objects.requireNonNull(parser, "Undefined parser");
  }

  @Override
  public String toString() {
    return getClass().getSimpleName() + " of " + parser.toString();
  }

  /**
   * Returns an {@link Iterator} over {@code parser} and all its reachable
   * descendants.
   */
  @Override
  public Iterator<Parser> iterator() {
    return new ParserIterator(parser);
  }

  private static class ParserIterator implements Iterator<Parser> {

    private final List<Parser> todo = new ArrayList<>();
    private final Set<Parser> seen = new HashSet<>();

    private ParserIterator(Parser root) {
      todo.add(root);
      seen.add(root);
    }

    @Override
    public boolean hasNext() {
      return !todo.isEmpty();
    }

    @Override
    public Parser next() {
      if (todo.isEmpty()) {
        throw new NoSuchElementException();
      }
      Parser current = todo.remove(todo.size() - 1);
      for (Parser parser : current.getChildren()) {
        if (!seen.contains(parser)) {
          todo.add(parser);
          seen.add(parser);
        }
      }
      return current;
    }
  }

  /**
   * Returns a {@link Stream} over {@code parser} and all its reachable
   * descendants.
   */
  public Stream<Parser> stream() {
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator(),
        Spliterator.DISTINCT | Spliterator.NONNULL), false);
  }

  /**
   * Returns a transformed copy of all parsers reachable from {@code parser}.
   */
  public Parser transform(Function<Parser, Parser> transformer) {
    Map<Parser, Parser> mapping = new HashMap<>();
    for (Parser parser : this) {
      mapping.put(parser, transformer.apply(parser.copy()));
    }
    Set<Parser> seen = new HashSet<>(mapping.values());
    List<Parser> todo = new ArrayList<>(mapping.values());
    while (!todo.isEmpty()) {
      Parser parent = todo.remove(todo.size() - 1);
      for (Parser child : parent.getChildren()) {
        if (mapping.containsKey(child)) {
          parent.replace(child, mapping.get(child));
        } else if (!seen.contains(child)) {
          seen.add(child);
          todo.add(child);
        }
      }
    }
    return mapping.get(parser);
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/Parser.java<|end_filename|>
package org.petitparser.parser;

import static org.petitparser.parser.primitive.CharacterParser.any;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.context.Token;
import org.petitparser.parser.actions.ActionParser;
import org.petitparser.parser.actions.ContinuationParser;
import org.petitparser.parser.actions.FlattenParser;
import org.petitparser.parser.actions.TokenParser;
import org.petitparser.parser.actions.TrimmingParser;
import org.petitparser.parser.combinators.AndParser;
import org.petitparser.parser.combinators.ChoiceParser;
import org.petitparser.parser.combinators.EndOfInputParser;
import org.petitparser.parser.combinators.NotParser;
import org.petitparser.parser.combinators.OptionalParser;
import org.petitparser.parser.combinators.SequenceParser;
import org.petitparser.parser.combinators.SettableParser;
import org.petitparser.parser.primitive.CharacterParser;
import org.petitparser.parser.repeating.GreedyRepeatingParser;
import org.petitparser.parser.repeating.LazyRepeatingParser;
import org.petitparser.parser.repeating.PossessiveRepeatingParser;
import org.petitparser.parser.repeating.RepeatingParser;
import org.petitparser.utils.Functions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

/**
 * An abstract parser that forms the root of all parsers in this package.
 */
public abstract class Parser {

  /**
   * Primitive method doing the actual parsing.
   *
   * <p>The method is overridden in concrete subclasses to implement the parser
   * specific logic. The methods takes a parse {@code context} and returns the
   * resulting context, which is either a
   * {@link org.petitparser.context.Success}
   * or {@link org.petitparser.context.Failure} context.
   */
  public abstract Result parseOn(Context context);

  /**
   * Primitive method doing the actual parsing.
   *
   * <p>This method is an optimized version of {@link #parseOn(Context)} that
   * is getting its speed advantage by avoiding any unnecessary memory
   * allocations.
   *
   * <p>The method is overridden in most concrete subclasses to implement the
   * optimized logic. As an input the method takes a {@code buffer} and the
   * current {@code position} in that buffer. It returns a new (positive)
   * position in case of a successful parse, or `-1` in case of a failure.
   *
   * <p>Subclasses don't necessarily have to override this method, since it is
   * emulated using its slower brother.
   */
  public int fastParseOn(String buffer, int position) {
    Result result = parseOn(new Context(buffer, position));
    return result.isSuccess() ? result.getPosition() : -1;
  }

  /**
   * Returns the parse result of the {@code input}.
   */
  public Result parse(String input) {
    return parseOn(new Context(input, 0));
  }

  /**
   * Tests if the {@code input} can be successfully parsed.
   */
  public boolean accept(String input) {
    return fastParseOn(input, 0) >= 0;
  }

  /**
   * Returns a list of all successful overlapping parses of the {@code input}.
   */
  @SuppressWarnings("unchecked")
  public <T> List<T> matches(String input) {
    List<Object> list = new ArrayList<>();
    and().mapWithSideEffects(list::add).seq(any()).or(any()).star()
        .fastParseOn(input, 0);
    return (List<T>) list;
  }

  /**
   * Returns a list of all successful non-overlapping parses of the {@code
   * input}.
   */
  @SuppressWarnings("unchecked")
  public <T> List<T> matchesSkipping(String input) {
    List<Object> list = new ArrayList<>();
    mapWithSideEffects(list::add).or(any()).star().fastParseOn(input, 0);
    return (List<T>) list;
  }

  /**
   * Returns new parser that accepts the receiver, if possible. The resulting
   * parser returns the result of the receiver, or {@code null} if not
   * applicable.
   */
  public Parser optional() {
    return optional(null);
  }

  /**
   * Returns new parser that accepts the receiver, if possible. The returned
   * value can be provided as {@code otherwise}.
   */
  public Parser optional(Object otherwise) {
    return new OptionalParser(this, otherwise);
  }

  /**
   * Returns a parser that accepts the receiver zero or more times. The
   * resulting parser returns a list of the parse results of the receiver.
   *
   * <p>This is a greedy and blind implementation that tries to consume as much
   * input as possible and that does not consider what comes afterwards.
   */
  public Parser star() {
    return repeat(0, RepeatingParser.UNBOUNDED);
  }

  /**
   * Returns a parser that parses the receiver zero or more times until it
   * reaches a {@code limit}.
   *
   * <p>This is a greedy non-blind implementation of the {@link Parser#star()}
   * operator. The {@code limit} is not consumed.
   */
  public Parser starGreedy(Parser limit) {
    return repeatGreedy(limit, 0, RepeatingParser.UNBOUNDED);
  }

  /**
   * Returns a parser that parses the receiver zero or more times until it
   * reaches a {@code limit}.
   *
   * <p>This is a lazy non-blind implementation of the {@link Parser#star()}
   * operator. The {@code limit} is not consumed.
   */
  public Parser starLazy(Parser limit) {
    return repeatLazy(limit, 0, RepeatingParser.UNBOUNDED);
  }

  /**
   * Returns a parser that accepts the receiver one or more times. The resulting
   * parser returns a list of the parse results of the receiver.
   *
   * <p>This is a greedy and blind implementation that tries to consume as much
   * input as possible and that does not consider what comes afterwards.
   */
  public Parser plus() {
    return repeat(1, RepeatingParser.UNBOUNDED);
  }

  /**
   * Returns a parser that parses the receiver one or more times until it
   * reaches {@code limit}.
   *
   * <p>This is a greedy non-blind implementation of the {@link Parser#plus()}
   * operator. The {@code limit} is not consumed.
   */
  public Parser plusGreedy(Parser limit) {
    return repeatGreedy(limit, 1, RepeatingParser.UNBOUNDED);
  }

  /**
   * Returns a parser that parses the receiver one or more times until it
   * reaches a {@code limit}.
   *
   * <p>This is a lazy non-blind implementation of the {@link Parser#plus()}
   * operator. The {@code limit} is not consumed.
   */
  public Parser plusLazy(Parser limit) {
    return repeatLazy(limit, 1, RepeatingParser.UNBOUNDED);
  }

  /**
   * Returns a parser that accepts the receiver between {@code min} and {@code
   * max} times. The resulting parser returns a list of the parse results of the
   * receiver.
   *
   * <p>This is a greedy and blind implementation that tries to consume as much
   * input as possible and that does not consider what comes afterwards.
   */
  public Parser repeat(int min, int max) {
    return new PossessiveRepeatingParser(this, min, max);
  }

  /**
   * Returns a parser that parses the receiver at least {@code min} and at most
   * {@code max} times until it reaches a {@code limit}.
   *
   * <p>This is a greedy non-blind implementation of the {@link
   * Parser#repeat(int, int)} operator. The {@code limit} is not consumed.
   */
  public Parser repeatGreedy(Parser limit, int min, int max) {
    return new GreedyRepeatingParser(this, limit, min, max);
  }

  /**
   * Returns a parser that parses the receiver at least {@code min} and at most
   * {@code max} times until it reaches a {@code limit}.
   *
   * <p>This is a lazy non-blind implementation of the {@link
   * Parser#repeat(int, int)} operator. The {@code limit} is not consumed.
   */
  public Parser repeatLazy(Parser limit, int min, int max) {
    return new LazyRepeatingParser(this, limit, min, max);
  }

  /**
   * Returns a parser that accepts the receiver exactly {@code count} times. The
   * resulting parser eturns a list of the parse results of the receiver.
   */
  public Parser times(int count) {
    return repeat(count, count);
  }

  /**
   * Returns a parser that accepts the receiver followed by {@code others}. The
   * resulting parser returns a list of the parse result of the receiver
   * followed by the parse result of {@code others}.
   *
   * <p>Calling this method on an existing sequence code not nest this sequence
   * into a new one, but instead augments the existing sequence with {@code
   * others}.
   */
  public SequenceParser seq(Parser... others) {
    Parser[] parsers = new Parser[1 + others.length];
    parsers[0] = this;
    System.arraycopy(others, 0, parsers, 1, others.length);
    return new SequenceParser(parsers);
  }

  /**
   * Returns a parser that accepts the receiver or {@code other}. The resulting
   * parser returns the parse result of the receiver, if the receiver fails it
   * returns the parse result of {@code other} (exclusive ordered choice).
   */
  public ChoiceParser or(Parser... others) {
    Parser[] parsers = new Parser[1 + others.length];
    parsers[0] = this;
    System.arraycopy(others, 0, parsers, 1, others.length);
    return new ChoiceParser(parsers);
  }

  /**
   * Returns a parser (logical and-predicate) that succeeds whenever the
   * receiver does, but never consumes input.
   */
  public Parser and() {
    return new AndParser(this);
  }

  /**
   * Returns a parser that is called with its current continuation.
   */
  public Parser callCC(ContinuationParser.ContinuationHandler handler) {
    return new ContinuationParser(this, handler);
  }

  /**
   * Returns a parser (logical not-predicate) that succeeds whenever the
   * receiver fails, but never consumes input.
   */
  public Parser not() {
    return not("unexpected");
  }

  /**
   * Returns a parser (logical not-predicate) that succeeds whenever the
   * receiver fails, but never consumes input.
   */
  public Parser not(String message) {
    return new NotParser(this, message);
  }

  /**
   * Returns a parser that consumes any input token (character), but the
   * receiver.
   */
  public Parser neg() {
    return neg(this + " not expected");
  }

  /**
   * Returns a parser that consumes any input token (character), but the
   * receiver.
   */
  public Parser neg(String message) {
    return not(message).seq(CharacterParser.any()).pick(1);
  }

  /**
   * Returns a parser that discards the result of the receiver, and instead
   * returns a sub-string of the consumed range in the buffer being parsed.
   */
  public Parser flatten() {
    return new FlattenParser(this);
  }

  /**
   * Returns a parser that discards the result of the receiver, and instead
   * returns a sub-string of the consumed range in the buffer being parsed.
   * Reports the provided {@code message} in case of an error.
   */
  public Parser flatten(String message) {
    return new FlattenParser(this, message);
  }

  /**
   * Returns a parser that returns a {@link Token}. The token carries the parsed
   * value of the receiver {@link Token#getValue()}, as well as the consumed
   * input {@link Token#getInput()} from  {@link Token#getStart()} to {@link
   * Token#getStop()} of the input being parsed.
   */
  public Parser token() {
    return new TokenParser(this);
  }

  /**
   * Returns a parser that consumes whitespace before and after the receiver.
   */
  public Parser trim() {
    return trim(CharacterParser.whitespace());
  }

  /**
   * Returns a parser that consumes input on {@code both} sides of the
   * receiver.
   */
  public Parser trim(Parser both) {
    return trim(both, both);
  }

  /**
   * Returns a parser that consumes input {@code before} and {@code after} the
   * receiver.
   */
  public Parser trim(Parser before, Parser after) {
    return new TrimmingParser(this, before, after);
  }

  /**
   * Returns a parser that succeeds only if the receiver consumes the complete
   * input.
   */
  public Parser end() {
    return end("end of input expected");
  }

  /**
   * Returns a parser that succeeds only if the receiver consumes the complete
   * input, otherwise return a failure with the {@code message}.
   */
  public Parser end(String message) {
    return new SequenceParser(this, new EndOfInputParser(message)).pick(0);
  }

  /**
   * Returns a parser that points to the receiver, but can be changed to point
   * to something else at a later point in time.
   */
  public SettableParser settable() {
    return SettableParser.with(this);
  }

  /**
   * Returns a parser that evaluates a {@code function} as the production action
   * on success of the receiver.
   *
   * @param function production action without side-effects.
   */
  public <A, B> Parser map(Function<A, B> function) {
    return new ActionParser<>(this, function);
  }

  /**
   * Returns a parser that evaluates a {@code function} as the production action
   * on success of the receiver.
   *
   * @param function production action with possible side-effects.
   */
  public <A, B> Parser mapWithSideEffects(Function<A, B> function) {
    return new ActionParser<>(this, function, true);
  }

  /**
   * Returns a parser that transform a successful parse result by returning the
   * element at {@code index} of a list. A negative index can be used to access
   * the elements from the back of the list.
   */
  public Parser pick(int index) {
    return map(Functions.nthOfList(index));
  }

  /**
   * Returns a parser that transforms a successful parse result by returning the
   * permuted elements at {@code indexes} of a list. Negative indexes can be
   * used to access the elements from the back of the list.
   */
  public Parser permute(int... indexes) {
    return this.map(Functions.permutationOfList(indexes));
  }

  /**
   * Returns a new parser that parses the receiver one or more times, separated
   * by a {@code separator}.
   */
  public Parser separatedBy(Parser separator) {
    return new SequenceParser(this, new SequenceParser(separator, this).star())
        .map((List<List<List<Object>>> input) -> {
          List<Object> result = new ArrayList<>();
          result.add(input.get(0));
          input.get(1).forEach(result::addAll);
          return result;
        });
  }

  /**
   * Returns a new parser that parses the receiver one or more times, separated
   * and possibly ended by a {@code separator}."
   */
  public Parser delimitedBy(Parser separator) {
    return separatedBy(separator).seq(separator.optional())
        .map((List<List<Object>> input) -> {
          List<Object> result = new ArrayList<>(input.get(0));
          if (input.get(1) != null) {
            result.add(input.get(1));
          }
          return result;
        });
  }

  /**
   * Returns a shallow copy of the receiver.
   */
  public abstract Parser copy();

  /**
   * Recursively tests for structural similarity of two parsers.
   *
   * <p>The code can automatically deals with recursive parsers and parsers
   * that refer to other parsers. This code is supposed to be overridden by
   * parsers that add other state.
   */
  public boolean isEqualTo(Parser other) {
    return isEqualTo(other, new HashSet<>());
  }

  /**
   * Recursively tests for structural similarity of two parsers.
   */
  protected boolean isEqualTo(Parser other, Set<Parser> seen) {
    if (this.equals(other) || seen.contains(this)) {
      return true;
    }
    seen.add(this);
    return Objects.equals(getClass(), other.getClass()) &&
        hasEqualProperties(other) && hasEqualChildren(other, seen);
  }

  /**
   * Compares the properties of two parsers.
   *
   * <p>Override this method in all subclasses that add new state.
   */
  protected boolean hasEqualProperties(Parser other) {
    return true;
  }

  /**
   * Compares the children of two parsers.
   *
   * <p>Normally subclasses should not override this method, but instead {@link
   * #getChildren()}.
   */
  protected boolean hasEqualChildren(Parser other, Set<Parser> seen) {
    List<Parser> thisChildren = this.getChildren();
    List<Parser> otherChildren = other.getChildren();
    if (thisChildren.size() != otherChildren.size()) {
      return false;
    }
    for (int i = 0; i < thisChildren.size(); i++) {
      if (!thisChildren.get(i).isEqualTo(otherChildren.get(i), seen)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns a list of directly referring parsers.
   */
  public List<Parser> getChildren() {
    return Collections.emptyList();
  }

  /**
   * Replaces the referring parser {@code source} with {@code target}. Does
   * nothing if the parser does not exist.
   */
  public void replace(Parser source, Parser target) {
    // no referring parsers
  }

  /**
   * Returns a human readable string identifying this parser.
   */
  public String toString() {
    return getClass().getSimpleName();
  }
}


<|start_filename|>petitparser-xml/src/main/java/module-info.java<|end_filename|>
module petitparser.xml {
  requires petitparser.core;
  exports org.petitparser.grammar.xml;
}

<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/combinators/SequenceParser.java<|end_filename|>
package org.petitparser.parser.combinators;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * A parser that parses a sequence of parsers.
 */
public class SequenceParser extends ListParser {

  public SequenceParser(Parser... parsers) {
    super(parsers);
  }

  @Override
  public Result parseOn(Context context) {
    Context current = context;
    List<Object> elements = new ArrayList<>(parsers.length);
    for (Parser parser : parsers) {
      Result result = parser.parseOn(current);
      if (result.isFailure()) {
        return result;
      }
      elements.add(result.get());
      current = result;
    }
    return current.success(elements);
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    for (Parser parser : parsers) {
      position = parser.fastParseOn(buffer, position);
      if (position < 0) {
        return position;
      }
    }
    return position;
  }

  @Override
  public SequenceParser seq(Parser... others) {
    Parser[] array = Arrays.copyOf(parsers, parsers.length + others.length);
    System.arraycopy(others, 0, array, parsers.length, others.length);
    return new SequenceParser(array);
  }

  @Override
  public SequenceParser copy() {
    return new SequenceParser(Arrays.copyOf(parsers, parsers.length));
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/repeating/GreedyRepeatingParser.java<|end_filename|>
package org.petitparser.parser.repeating;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

import java.util.ArrayList;
import java.util.List;

/**
 * A greedy repeating parser, commonly seen in regular expression
 * implementations. It aggressively consumes as much input as possible and then
 * backtracks to meet the 'limit' condition.
 */
public class GreedyRepeatingParser extends LimitedRepeatingParser {

  public GreedyRepeatingParser(
      Parser delegate, Parser limit, int min, int max) {
    super(delegate, limit, min, max);
  }

  @Override
  public Result parseOn(Context context) {
    Context current = context;
    List<Object> elements = new ArrayList<>();
    while (elements.size() < min) {
      Result result = delegate.parseOn(current);
      if (result.isFailure()) {
        return result;
      }
      elements.add(result.get());
      current = result;
    }
    List<Context> contexts = new ArrayList<>();
    contexts.add(current);
    while (max == UNBOUNDED || elements.size() < max) {
      Result result = delegate.parseOn(current);
      if (result.isFailure()) {
        break;
      }
      elements.add(result.get());
      contexts.add(current = result);
    }
    while (true) {
      Result limiter = limit.parseOn(contexts.get(contexts.size() - 1));
      if (limiter.isSuccess()) {
        return contexts.get(contexts.size() - 1).success(elements);
      }
      if (elements.isEmpty()) {
        return limiter;
      }
      contexts.remove(contexts.size() - 1);
      elements.remove(elements.size() - 1);
      if (contexts.isEmpty()) {
        return limiter;
      }
    }
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    int count = 0;
    int current = position;
    while (count < min) {
      int result = delegate.fastParseOn(buffer, current);
      if (result < 0) {
        return -1;
      }
      current = result;
      count++;
    }
    List<Integer> positions = new ArrayList<>();
    positions.add(current);
    while (max == UNBOUNDED || count < max) {
      int result = delegate.fastParseOn(buffer, current);
      if (result < 0) {
        break;
      }
      positions.add(current = result);
      count++;
    }
    while (true) {
      int limiter =
          limit.fastParseOn(buffer, positions.get(positions.size() - 1));
      if (limiter >= 0) {
        return positions.get(positions.size() - 1);
      }
      if (count == 0) {
        return -1;
      }
      positions.remove(positions.size() - 1);
      count--;
      if (positions.isEmpty()) {
        return -1;
      }
    }
  }

  @Override
  public GreedyRepeatingParser copy() {
    return new GreedyRepeatingParser(delegate, limit, min, max);
  }
}



<|start_filename|>petitparser-core/src/main/java/org/petitparser/utils/Tracer.java<|end_filename|>
package org.petitparser.utils;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Traces the activation and return of parsers.
 */
public class Tracer {

  /**
   * Returns a parser that calls the provided consumer with a {@link TraceEvent}
   * whenever a parser is activated or returning.
   */
  public static Parser on(Parser source, Consumer<TraceEvent> consumer) {
    TraceEvent[] parentClosure = new TraceEvent[1];
    return Mirror.of(source)
        .transform(parser -> parser.callCC((continuation, context) -> {
          TraceEvent parent = parentClosure[0];
          TraceEvent enter =
              new TraceEvent(TraceEventType.ENTER, parent, parser, context);
          consumer.accept(enter);
          parentClosure[0] = enter;
          Result result = continuation.apply(context);
          parentClosure[0] = parent;
          TraceEvent exit =
              new TraceEvent(TraceEventType.EXIT, parent, parser, result);
          consumer.accept(exit);
          return result;
        }));
  }

  /**
   * The trace event type differentiating between activation and return.
   */
  public enum TraceEventType {
    ENTER, EXIT
  }

  /**
   * The trace event holding all relevant data.
   */
  public static class TraceEvent {

    /**
     * The type of this event.
     */
    public final TraceEventType type;

    /**
     * The parent of this event.
     */
    public final TraceEvent parent;

    /**
     * The parser being traced.
     */
    public final Parser parser;

    /**
     * The current parser context.
     */
    public final Context context;

    /**
     * The current invocation level.
     */
    public int getLevel() {
      return parent != null ? 1 + parent.getLevel() : 0;
    }

    private TraceEvent(
        TraceEventType type, TraceEvent parent, Parser parser,
        Context context) {
      this.type = type;
      this.parent = parent;
      this.parser = parser;
      this.context = context;
    }

    @Override
    public String toString() {
      String indent = Stream.generate(() -> "  ").limit(getLevel())
          .collect(Collectors.joining());
      return indent +
          (Objects.equals(TraceEventType.ENTER, type) ? parser : context);
    }
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/tools/GrammarParser.java<|end_filename|>
package org.petitparser.tools;

import org.petitparser.parser.combinators.DelegateParser;

/**
 * A helper to build a parser from a {@link GrammarDefinition}.
 */
public class GrammarParser extends DelegateParser {

  public GrammarParser(GrammarDefinition definition) {
    super(definition.build());
  }

  public GrammarParser(GrammarDefinition definition, String name) {
    super(definition.build(name));
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    return delegate.fastParseOn(buffer, position);
  }
}

<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlName.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import java.util.Objects;

/**
 * XML entity name.
 */
public class XmlName implements Cloneable {

  private final String prefix;
  private final String local;

  public XmlName(String name) {
    int index = name.indexOf(':');
    if (index < 0) {
      this.prefix = null;
      this.local = name;
    } else {
      this.prefix = name.substring(0, index);
      this.local = name.substring(index + 1, name.length());
    }
  }

  public String getLocal() {
    return local;
  }

  public String getPrefix() {
    return prefix;
  }

  public String getQualified() {
    return toXmlString();
  }

  @Override
  public String toString() {
    return super.toString() + " (" + toXmlString() + ")";
  }

  public String toXmlString() {
    StringBuilder buffer = new StringBuilder();
    writeTo(buffer);
    return buffer.toString();
  }

  public void writeTo(StringBuilder buffer) {
    if (prefix != null) {
      buffer.append(prefix).append(':');
    }
    buffer.append(local);
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null || getClass() != obj.getClass()) {
      return false;
    }
    XmlName other = (XmlName) obj;
    return Objects.equals(prefix, other.prefix) &&
        Objects.equals(local, other.local);
  }

  @Override
  public int hashCode() {
    return Objects.hash(prefix, local);
  }
}


<|start_filename|>petitparser-json/src/main/java/org/petitparser/grammar/json/JsonParserDefinition.java<|end_filename|>
package org.petitparser.grammar.json;

import org.petitparser.utils.Functions;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * JSON parser definition.
 */
public class JsonParserDefinition extends JsonGrammarDefinition {

  public JsonParserDefinition() {
    action("elements", Functions.withoutSeparators());
    action("members", Functions.withoutSeparators());
    action("array", (List<List<?>> input) -> {
      return input.get(1) != null ? input.get(1) : new ArrayList<>();
    });
    action("object", (List<List<List<Object>>> input) -> {
      Map<Object, Object> result = new LinkedHashMap<>();
      if (input.get(1) != null) {
        for (List<Object> list : input.get(1)) {
          result.put(list.get(0), list.get(2));
        }
      }
      return result;
    });

    action("trueToken", Functions.constant(true));
    action("falseToken", Functions.constant(false));
    action("nullToken", Functions.constant(null));
    redef("stringToken", ref("stringPrimitive").trim());
    action("numberToken", (String input) -> {
      double floating = Double.parseDouble(input);
      long integral = (long) floating;
      if (floating == integral && input.indexOf('.') == -1) {
        return integral;
      } else {
        return floating;
      }
    });

    action("stringPrimitive",
        (List<List<Character>> input) -> listToString(input.get(1)));
    action("characterEscape", Functions.lastOfList());
    action("characterEscape", ESCAPE_TABLE_FUNCTION);
    action("characterOctal", (List<String> input) -> {
      // cannot be larger than 0xFFFF, so we should be safe with 16-bit
      return Character.toChars(Integer.parseInt(input.get(1), 16))[0];
    });
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlComment.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import org.petitparser.grammar.xml.XmlDefinition;

/**
 * An XML comment node.
 */
public class XmlComment extends XmlData {

  public XmlComment(String data) {
    super(data);
  }

  @Override
  public void writeTo(StringBuilder buffer) {
    buffer.append(XmlDefinition.OPEN_COMMENT);
    buffer.append(getData());
    buffer.append(XmlDefinition.CLOSE_COMMENT);
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/XmlParser.java<|end_filename|>
package org.petitparser.grammar.xml;

import org.petitparser.tools.GrammarParser;

/**
 * XmlParser Builder
 */
public class XmlParser extends GrammarParser {

  public XmlParser() {
    super(new XmlDefinition<>(new XmlBuilder()));
  }
}


<|start_filename|>petitparser-smalltalk/src/main/java/module-info.java<|end_filename|>
module petitparser.smalltalk {
  requires petitparser.core;
  exports org.petitparser.grammar.smalltalk;
}

<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/package-info.java<|end_filename|>
/**
 * This package provides a simple XML model and its associated parser.
 */
package org.petitparser.grammar.xml;


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/repeating/LazyRepeatingParser.java<|end_filename|>
package org.petitparser.parser.repeating;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

import java.util.ArrayList;
import java.util.List;

/**
 * A lazy repeating parser, commonly seen in regular expression implementations.
 * It limits its consumption to meet the 'limit' condition as early as
 * possible.
 */
public class LazyRepeatingParser extends LimitedRepeatingParser {

  public LazyRepeatingParser(Parser delegate, Parser limit, int min, int max) {
    super(delegate, limit, min, max);
  }

  @Override
  public Result parseOn(Context context) {
    Context current = context;
    List<Object> elements = new ArrayList<>();
    while (elements.size() < min) {
      Result result = delegate.parseOn(current);
      if (result.isFailure()) {
        return result;
      }
      elements.add(result.get());
      current = result;
    }
    while (true) {
      Result limiter = limit.parseOn(current);
      if (limiter.isSuccess()) {
        return current.success(elements);
      } else {
        if (max != UNBOUNDED && elements.size() >= max) {
          return limiter;
        }
        Result result = delegate.parseOn(current);
        if (result.isFailure()) {
          return limiter;
        }
        elements.add(result.get());
        current = result;
      }
    }
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    int count = 0;
    int current = position;
    while (count < min) {
      int result = delegate.fastParseOn(buffer, current);
      if (result < 0) {
        return -1;
      }
      current = result;
      count++;
    }
    while (true) {
      int limiter = limit.fastParseOn(buffer, current);
      if (limiter >= 0) {
        return current;
      } else {
        if (max != UNBOUNDED && count >= max) {
          return -1;
        }
        int result = delegate.fastParseOn(buffer, current);
        if (result < 0) {
          return -1;
        }
        current = result;
        count++;
      }
    }
  }

  @Override
  public LazyRepeatingParser copy() {
    return new LazyRepeatingParser(delegate, limit, min, max);
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/actions/TrimmingParser.java<|end_filename|>
package org.petitparser.parser.actions;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;
import org.petitparser.parser.combinators.DelegateParser;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * A parser that silently consumes a before and after the delegate parser.
 */
public class TrimmingParser extends DelegateParser {

  private Parser left;
  private Parser right;

  public TrimmingParser(Parser delegate, Parser left, Parser right) {
    super(delegate);
    this.left = Objects.requireNonNull(left, "Undefined left trimming parser");
    this.right =
        Objects.requireNonNull(right, "Undefined right trimming parser");
  }

  @Override
  public Result parseOn(Context context) {
    String buffer = context.getBuffer();

    // Trim the left part:
    int before = consume(left, buffer, context.getPosition());
    if (before != context.getPosition()) {
      context = new Context(buffer, before);
    }

    // Consume the delegate:
    Result result = delegate.parseOn(context);
    if (result.isFailure()) {
      return result;
    }

    // Trim the right part:
    int after = consume(right, buffer, result.getPosition());
    return after == result.getPosition() ? result :
        result.success(result.get(), after);
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    int result = delegate.fastParseOn(buffer, consume(left, buffer, position));
    return result < 0 ? result : consume(right, buffer, result);
  }

  private int consume(Parser parser, String buffer, int position) {
    for (; ; ) {
      int result = parser.fastParseOn(buffer, position);
      if (result < 0) {
        return position;
      }
      position = result;
    }
  }

  @Override
  public void replace(Parser source, Parser target) {
    super.replace(source, target);
    if (left == source) {
      left = target;
    }
    if (right == source) {
      right = target;
    }
  }

  @Override
  public List<Parser> getChildren() {
    return Arrays.asList(delegate, left, right);
  }

  @Override
  public TrimmingParser copy() {
    return new TrimmingParser(delegate, left, right);
  }
}


<|start_filename|>petitparser-json/src/main/java/org/petitparser/grammar/json/JsonParser.java<|end_filename|>
package org.petitparser.grammar.json;

import org.petitparser.tools.GrammarParser;

/**
 * JSON parser.
 */
public class JsonParser extends GrammarParser {
  public JsonParser() {
    super(new JsonParserDefinition());
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/combinators/EndOfInputParser.java<|end_filename|>
package org.petitparser.parser.combinators;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

import java.util.Objects;

/**
 * A parser that succeeds only at the end of the input stream.
 */
public class EndOfInputParser extends Parser {

  protected final String message;

  public EndOfInputParser(String message) {
    this.message = Objects.requireNonNull(message, "Undefined message");
  }

  @Override
  public Result parseOn(Context context) {
    return context.getPosition() < context.getBuffer().length() ?
        context.failure(message) : context.success(null);
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    return position < buffer.length() ? -1 : position;
  }

  @Override
  protected boolean hasEqualProperties(Parser other) {
    return super.hasEqualProperties(other) &&
        Objects.equals(message, ((EndOfInputParser) other).message);
  }

  @Override
  public EndOfInputParser copy() {
    return new EndOfInputParser(message);
  }

  @Override
  public String toString() {
    return super.toString() + "[" + message + "]";
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/repeating/LimitedRepeatingParser.java<|end_filename|>
package org.petitparser.parser.repeating;

import org.petitparser.parser.Parser;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * An abstract parser that repeatedly parses between 'min' and 'max' instances
 * of its delegate and that requires the input to be completed with a specified
 * parser 'limit'. Subclasses provide repeating behavior as typically seen in
 * regular expression implementations (non-blind).
 */
public abstract class LimitedRepeatingParser extends RepeatingParser {

  protected Parser limit;

  public LimitedRepeatingParser(
      Parser delegate, Parser limit, int min, int max) {
    super(delegate, min, max);
    this.limit = Objects.requireNonNull(limit, "Undefined limit parser");
  }

  @Override
  public List<Parser> getChildren() {
    return Arrays.asList(delegate, limit);
  }

  @Override
  public void replace(Parser source, Parser target) {
    super.replace(source, target);
    if (limit == source) {
      limit = target;
    }
  }
}


<|start_filename|>petitparser-core/src/test/java/org/petitparser/EqualityTest.java<|end_filename|>
package org.petitparser;

import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.petitparser.parser.primitive.CharacterParser.of;

import org.junit.Test;
import org.petitparser.parser.Parser;
import org.petitparser.parser.combinators.DelegateParser;
import org.petitparser.parser.primitive.CharacterParser;
import org.petitparser.parser.primitive.EpsilonParser;
import org.petitparser.parser.primitive.FailureParser;
import org.petitparser.parser.primitive.StringParser;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * Tests {@link Parser#copy}, {@link Parser#equals(Object)}, and {@link
 * Parser#replace(Parser, Parser)}.
 */
public class EqualityTest {

  private void verify(Parser parser) {
    Parser copy = parser.copy();
    // check copying
    assertNotSame(parser, copy);
    assertEquals(parser.getClass(), copy.getClass());
    assertEquals(parser.getChildren().size(), copy.getChildren().size());
    assertPairwiseSame(parser.getChildren(), copy.getChildren());
    assertEquals(parser.toString(), copy.toString());
    // check equality
    assertTrue(copy.isEqualTo(copy));
    assertTrue(parser.isEqualTo(copy));
    assertTrue(copy.isEqualTo(parser));
    assertTrue(parser.isEqualTo(parser));
    // check replacing
    List<Parser> replaced = new ArrayList<>();
    for (int i = 0; i < copy.getChildren().size(); i++) {
      Parser source = copy.getChildren().get(i);
      Parser target = CharacterParser.any();
      copy.replace(source, target);
      assertSame(target, copy.getChildren().get(i));
      replaced.add(target);
    }
    assertPairwiseSame(replaced, copy.getChildren());
  }

  private void assertPairwiseSame(List<Parser> expected, List<Parser> actual) {
    assertEquals(expected.size(), actual.size());
    for (int i = 0; i < expected.size(); i++) {
      assertSame(expected.get(i), actual.get(i));
    }
  }

  @Test
  public void differentChildren() {
    Parser first = of('a').or(of('b')).or(of('c'));
    Parser second = of('a').or(of('b')).or(of('d'));
    assertFalse(first.isEqualTo(second));
    assertFalse(second.isEqualTo(first));
  }

  @Test
  public void differentSize() {
    Parser first = of('a').or(of('b')).or(of('c'));
    Parser second = of('a').or(of('b'));
    assertFalse(first.isEqualTo(second));
    assertFalse(second.isEqualTo(first));
  }

  @Test
  public void any() {
    verify(CharacterParser.any());
  }

  @Test
  public void and() {
    verify(CharacterParser.digit().and());
  }

  @Test
  public void is() {
    verify(CharacterParser.of('a'));
  }

  @Test
  public void digit() {
    verify(CharacterParser.digit());
  }

  @Test
  public void delegate() {
    verify(new DelegateParser(CharacterParser.any()));
  }

  @Test
  public void continuation() {
    verify(CharacterParser.digit().callCC((continuation, context) -> null));
  }

  @Test
  public void end() {
    verify(CharacterParser.digit().end());
  }

  @Test
  public void epsilon() {
    verify(new EpsilonParser());
  }

  @Test
  public void failure() {
    verify(FailureParser.withMessage("failure"));
  }

  @Test
  public void flatten1() {
    verify(CharacterParser.digit().flatten());
  }

  @Test
  public void flatten2() {
    verify(CharacterParser.digit().flatten("digit"));
  }

  @Test
  public void map() {
    verify(CharacterParser.digit().map(Function.identity()));
  }

  @Test
  public void not() {
    verify(CharacterParser.digit().not());
  }

  @Test
  public void optional() {
    verify(CharacterParser.digit().optional());
  }

  @Test
  public void or() {
    verify(CharacterParser.digit().or(CharacterParser.word()));
  }

  @Test
  public void plus() {
    verify(CharacterParser.digit().plus());
  }

  @Test
  public void plusGreedy() {
    verify(CharacterParser.digit().plusGreedy(CharacterParser.word()));
  }

  @Test
  public void plusLazy() {
    verify(CharacterParser.digit().plusLazy(CharacterParser.word()));
  }

  @Test
  public void repeat() {
    verify(CharacterParser.digit().repeat(2, 3));
  }

  @Test
  public void repeatGreedy() {
    verify(CharacterParser.digit().repeatGreedy(CharacterParser.word(), 2, 3));
  }

  @Test
  public void repeatLazy() {
    verify(CharacterParser.digit().repeatLazy(CharacterParser.word(), 2, 3));
  }

  @Test
  public void seq() {
    verify(CharacterParser.digit().seq(CharacterParser.word()));
  }

  @Test
  public void settable() {
    verify(CharacterParser.digit().settable());
  }

  @Test
  public void star() {
    verify(CharacterParser.digit().star());
  }

  @Test
  public void starGreedy() {
    verify(CharacterParser.digit().starGreedy(CharacterParser.word()));
  }

  @Test
  public void starLazy() {
    verify(CharacterParser.digit().starLazy(CharacterParser.word()));
  }

  @Test
  public void string() {
    verify(StringParser.of("ab"));
  }

  @Test
  public void stringIgnoringCase() {
    verify(StringParser.ofIgnoringCase("ab"));
  }

  @Test
  public void times() {
    verify(CharacterParser.digit().times(2));
  }

  @Test
  public void token() {
    verify(CharacterParser.digit().token());
  }

  @Test
  public void trim() {
    verify(CharacterParser.digit()
        .trim(CharacterParser.of('a'), CharacterParser.of('b')));
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/XmlCallback.java<|end_filename|>
package org.petitparser.grammar.xml;

import java.util.Collection;

/**
 * Interface with callbacks form the {@link XmlDefinition}.
 */
public interface XmlCallback<TName, TNode, TAttribute> {

  TNode createAttribute(TName name, String text);

  TNode createComment(String text);

  TNode createCDATA(String text);

  TNode createDoctype(String text);

  TNode createDocument(Collection<TNode> children);

  TNode createElement(
      TName name, Collection<TAttribute> attributes,
      Collection<TNode> children);

  TNode createProcessing(String target, String text);

  TName createQualified(String name);

  TNode createText(String text);
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/context/Success.java<|end_filename|>
package org.petitparser.context;

/**
 * An immutable parse success.
 */
public class Success extends Result {

  private final Object result;

  public Success(String buffer, int position, Object result) {
    super(buffer, position);
    this.result = result;
  }

  @Override
  public boolean isSuccess() {
    return true;
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T> T get() {
    return (T) result;
  }

  @Override
  public String toString() {
    return super.toString() + ": " + result;
  }
}


<|start_filename|>petitparser-core/src/main/java/module-info.java<|end_filename|>
module petitparser.core {
  exports org.petitparser.context;
  exports org.petitparser.parser;
  exports org.petitparser.parser.actions;
  exports org.petitparser.parser.combinators;
  exports org.petitparser.parser.primitive;
  exports org.petitparser.parser.repeating;
  exports org.petitparser.tools;
  exports org.petitparser.utils;
}

<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlElement.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import org.petitparser.grammar.xml.XmlDefinition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * XML element node.
 */
public class XmlElement extends XmlParent {

  private final XmlName name;
  private final List<XmlAttribute> attributes;

  public XmlElement(
      XmlName name, Collection<XmlAttribute> attributes,
      Collection<XmlNode> children) {
    super(children);
    this.name = name;
    this.attributes = new ArrayList<>(attributes);
    for (XmlAttribute attribute : attributes) {
      attribute.setParent(this);
    }
  }

  public XmlName getName() {
    return name;
  }

  @Override
  public List<XmlAttribute> getAttributes() {
    return attributes;
  }

  public String getAttribute(String key) {
    XmlAttribute attribute = getAttributeNode(key);
    return attribute != null ? attribute.getValue() : null;
  }

  public XmlAttribute getAttributeNode(String key) {
    for (XmlAttribute attribute : attributes) {
      if (Objects.equals(attribute.getName().getLocal(), key)) {
        return attribute;
      }
    }
    return null;
  }

  @Override
  public void writeTo(StringBuilder buffer) {
    buffer.append(XmlDefinition.OPEN_ELEMENT);
    getName().writeTo(buffer);
    for (XmlAttribute attribute : getAttributes()) {
      buffer.append(XmlDefinition.WHITESPACE);
      attribute.writeTo(buffer);
    }
    if (getChildren().isEmpty()) {
      buffer.append(XmlDefinition.WHITESPACE);
      buffer.append(XmlDefinition.CLOSE_END_ELEMENT);
    } else {
      buffer.append(XmlDefinition.CLOSE_ELEMENT);
      super.writeTo(buffer);
      buffer.append(XmlDefinition.OPEN_END_ELEMENT);
      getName().writeTo(buffer);
      buffer.append(XmlDefinition.CLOSE_ELEMENT);
    }
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!super.equals(obj) || getClass() != obj.getClass()) {
      return false;
    }
    XmlElement other = (XmlElement) obj;
    return Objects.equals(name, other.name) &&
        Objects.equals(attributes, other.attributes);
  }

  @Override
  public int hashCode() {
    return Objects.hash(super.hashCode(), name, attributes.size());
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/context/ParseError.java<|end_filename|>
package org.petitparser.context;

/**
 * A parse error.
 */
public class ParseError extends RuntimeException {

  private final Failure failure;

  public ParseError(Failure failure) {
    super(failure.getMessage());
    this.failure = failure;
  }

  /**
   * Return the parse {@link Context} causing this error.
   */
  public Failure getFailure() {
    return failure;
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlCdata.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import org.petitparser.grammar.xml.XmlDefinition;

/**
 * XML text node.
 */
public class XmlCdata extends XmlData {

  public XmlCdata(String data) {
    super(data);
  }

  @Override
  public void writeTo(StringBuilder buffer) {
    buffer.append(XmlDefinition.OPEN_CDATA);
    buffer.append(getData());
    buffer.append(XmlDefinition.CLOSE_CDATA);
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/context/Failure.java<|end_filename|>
package org.petitparser.context;

/**
 * An immutable parse failure.
 */
public class Failure extends Result {

  private final String message;

  public Failure(String buffer, int position, String message) {
    super(buffer, position);
    this.message = message;
  }

  @Override
  public String getMessage() {
    return message;
  }

  @Override
  public boolean isFailure() {
    return true;
  }

  @Override
  public <T> T get() {
    throw new ParseError(this);
  }

  @Override
  public String toString() {
    return super.toString() + ": " + message;
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/utils/Optimizer.java<|end_filename|>
package org.petitparser.utils;

import org.petitparser.parser.Parser;
import org.petitparser.parser.combinators.DelegateParser;
import org.petitparser.parser.combinators.SettableParser;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

/**
 * Tools to transform and optimize parser graphs.
 */
public class Optimizer {

  private final List<Function<Parser, Parser>> transformers = new ArrayList<>();

  /**
   * Adds a generic transformer.
   */
  public Optimizer add(Function<Parser, Parser> transformer) {
    transformers.add(transformer);
    return this;
  }

  /**
   * Adds a transformer that removes unnecessary delegates.
   */
  public Optimizer removeDelegates() {
    return add(parser -> {
      while (DelegateParser.class.equals(parser.getClass()) ||
          SettableParser.class.equals(parser.getClass())) {
        parser = parser.getChildren().get(0);
      }
      return parser;
    });
  }

  /**
   * Adds a transformer that collapses unnecessary copies of parsers.
   */
  public Optimizer removeDuplicates() {
    Set<Parser> uniques = new HashSet<>();
    return add(parser -> {
      Optional<Parser> target = uniques.stream()
          .filter(each -> parser != each && parser.isEqualTo(each)).findFirst();
      if (target.isPresent()) {
        return target.get();
      } else {
        uniques.add(parser);
        return parser;
      }
    });
  }

  /**
   * Transforms the provided parsers using the selected optimizations.
   */
  public Parser transform(Parser parser) {
    Function<Parser, Parser> transformer =
        transformers.stream().reduce(Function::andThen)
            .orElse(Function.identity());
    return Mirror.of(parser).transform(transformer);
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlProcessing.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import org.petitparser.grammar.xml.XmlDefinition;

import java.util.Objects;

/**
 * XML processing instruction.
 */
public class XmlProcessing extends XmlData {

  private final String target;

  public XmlProcessing(String target, String data) {
    super(data);
    this.target = target;
  }

  public String getTarget() {
    return target;
  }

  @Override
  public void writeTo(StringBuilder buffer) {
    buffer.append(XmlDefinition.OPEN_PROCESSING);
    buffer.append(getTarget());
    if (!getData().isEmpty()) {
      buffer.append(XmlDefinition.WHITESPACE);
      buffer.append(getData());
    }
    buffer.append(XmlDefinition.CLOSE_PROCESSING);
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (!super.equals(obj) || getClass() != obj.getClass()) {
      return false;
    }
    XmlProcessing other = (XmlProcessing) obj;
    return Objects.equals(target, other.target);
  }

  @Override
  public int hashCode() {
    return Objects.hash(super.hashCode(), getTarget());
  }
}


<|start_filename|>petitparser-json/src/main/java/module-info.java<|end_filename|>
module petitparser.json {
  requires petitparser.core;
  exports org.petitparser.grammar.json;
}

<|start_filename|>petitparser-core/src/test/java/org/petitparser/utils/MirrorTest.java<|end_filename|>
package org.petitparser.utils;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.petitparser.parser.combinators.SettableParser.undefined;
import static org.petitparser.parser.primitive.CharacterParser.lowerCase;
import static org.petitparser.parser.primitive.CharacterParser.upperCase;

import org.junit.Test;
import org.petitparser.parser.Parser;
import org.petitparser.parser.combinators.SettableParser;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Tests {@link Mirror}.
 */
public class MirrorTest {

  @Test
  public void testToString() {
    Parser parser = lowerCase();
    Mirror mirror = Mirror.of(parser);
    assertEquals("CharacterParser[lowercase letter expected]",
        parser.toString());
    assertEquals("Mirror of CharacterParser[lowercase letter expected]",
        mirror.toString());
  }

  @Test
  public void testSingleElementIteration() {
    Parser parser = lowerCase();
    Mirror mirror = Mirror.of(parser);
    List<Parser> parsers = mirror.stream().collect(Collectors.toList());
    assertEquals(Arrays.asList(parser), parsers);
  }

  @Test
  public void testNestedElementsIteration() {
    Parser parser3 = lowerCase();
    Parser parser2 = parser3.star();
    Parser parser1 = parser2.flatten();
    Mirror mirror = Mirror.of(parser1);
    List<Parser> parsers = mirror.stream().collect(Collectors.toList());
    assertEquals(Arrays.asList(parser1, parser2, parser3), parsers);
  }

  @Test
  public void testBranchedElementsIteration() {
    Parser parser3 = lowerCase();
    Parser parser2 = upperCase();
    Parser parser1 = parser2.seq(parser3);
    Mirror mirror = Mirror.of(parser1);
    List<Parser> parsers = mirror.stream().collect(Collectors.toList());
    assertEquals(Arrays.asList(parser1, parser3, parser2), parsers);
  }

  @Test
  public void testDuplicatedElementsIteration() {
    Parser parser2 = upperCase();
    Parser parser1 = parser2.seq(parser2);
    Mirror mirror = Mirror.of(parser1);
    List<Parser> parsers = mirror.stream().collect(Collectors.toList());
    assertEquals(Arrays.asList(parser1, parser2), parsers);
  }

  @Test
  public void testKnotParserIteration() {
    SettableParser parser1 = undefined();
    parser1.set(parser1);
    Mirror mirror = Mirror.of(parser1);
    List<Parser> parsers = mirror.stream().collect(Collectors.toList());
    assertEquals(Arrays.asList(parser1), parsers);
  }

  @Test
  public void testLoopingParserIteration() {
    SettableParser parser1 = undefined();
    SettableParser parser2 = undefined();
    SettableParser parser3 = undefined();
    parser1.set(parser2);
    parser2.set(parser3);
    parser3.set(parser1);
    Mirror mirror = Mirror.of(parser1);
    List<Parser> parsers = mirror.stream().collect(Collectors.toList());
    assertEquals(Arrays.<Parser>asList(parser1, parser2, parser3), parsers);
  }

  @Test
  public void testBasicIteration() {
    Parser parser = lowerCase();
    Iterator<Parser> iterator = Mirror.of(parser).iterator();
    assertTrue(iterator.hasNext());
    assertEquals(parser, iterator.next());
    assertFalse(iterator.hasNext());
    try {
      iterator.next();
      fail();
    } catch (NoSuchElementException exception) {
      // expected
    }
  }

  @Test
  public void testIdentityTransformation() {
    Parser input = lowerCase().settable();
    Parser output = Mirror.of(input).transform(Function.identity());
    assertNotEquals(input, output);
    assertTrue(input.isEqualTo(output));
    assertNotEquals(input.getChildren().get(0), output.getChildren().get(0));
  }

  @Test
  public void testReplaceRootTransformation() {
    Parser source = lowerCase();
    Parser target = upperCase();
    Parser output = Mirror.of(source)
        .transform(parser -> source.isEqualTo(parser) ? target : parser);
    assertNotEquals(source, output);
    assertFalse(source.isEqualTo(output));
    assertEquals(output, target);
  }

  @Test
  public void testSingleElementTransformation() {
    Parser source = lowerCase();
    Parser input = source.settable();
    Parser target = upperCase();
    Parser output = Mirror.of(input)
        .transform(parser -> source.isEqualTo(parser) ? target : parser);
    assertNotEquals(input, output);
    assertFalse(input.isEqualTo(output));
    assertEquals(input.getChildren().get(0), source);
    assertEquals(output.getChildren().get(0), target);
  }

  @Test
  public void testDoubleElementTransformation() {
    Parser source = lowerCase();
    Parser input = source.seq(source);
    Parser target = upperCase();
    Parser output = Mirror.of(input)
        .transform(parser -> source.isEqualTo(parser) ? target : parser);
    assertNotEquals(input, output);
    assertFalse(input.isEqualTo(output));
    assertTrue(input.isEqualTo(source.seq(source)));
    assertEquals(input.getChildren().get(0), input.getChildren().get(1));
    assertTrue(output.isEqualTo(target.seq(target)));
    assertEquals(output.getChildren().get(0), output.getChildren().get(1));
  }

  @Test
  public void testExistingLoopTransformation() {
    Parser input = undefined().settable().settable().settable();
    SettableParser settable =
        (SettableParser) input.getChildren().get(0).getChildren().get(0);
    settable.set(input);
    Parser output = Mirror.of(input).transform(Function.identity());
    assertNotEquals(input, output);
    assertTrue(input.isEqualTo(output));
    Set<Parser> inputs = Mirror.of(input).stream().collect(Collectors.toSet());
    Set<Parser> outputs =
        Mirror.of(output).stream().collect(Collectors.toSet());
    inputs.forEach(each -> assertFalse(outputs.contains(each)));
    outputs.forEach(each -> assertFalse(inputs.contains(each)));
  }

  @Test
  public void testNewLoopTransformation() {
    Parser source = lowerCase();
    Parser target = undefined().settable().settable().settable();
    SettableParser settable =
        (SettableParser) target.getChildren().get(0).getChildren().get(0);
    settable.set(source);
    Parser output = Mirror.of(source)
        .transform(parser -> source.isEqualTo(parser) ? target : parser);
    assertNotEquals(source, output);
    assertFalse(source.isEqualTo(output));
    assertTrue(output.isEqualTo(target));
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlData.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import java.util.Objects;

/**
 * Abstract XML data node.
 */
public abstract class XmlData extends XmlNode {

  private final String data;

  public XmlData(String data) {
    this.data = data;
  }

  public String getData() {
    return data;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null || getClass() != obj.getClass()) {
      return false;
    }
    XmlData other = (XmlData) obj;
    return Objects.equals(data, other.data);
  }

  @Override
  public int hashCode() {
    return data.hashCode();
  }
}


<|start_filename|>petitparser-json/src/test/java/org/petitparser/grammar/json/JsonParserTest.java<|end_filename|>
package org.petitparser.grammar.json;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

import java.util.List;
import java.util.Map;

/**
 * Tests {@link JsonParser}.
 */
public class JsonParserTest {

  private final static Parser parser = new JsonParser();
  private final static double EPSILON = 1.0e-6;

  // arrays

  @Test
  public void testEmptyArray() {
    List<String> result = assertValid("[]").get();
    assertTrue(result.isEmpty());
  }

  @Test
  public void testSmallArray() {
    List<String> result = assertValid("[\"a\"]").get();
    assertEquals(1, result.size());
    assertEquals("a", result.get(0));
  }

  @Test
  public void testBigArray() {
    List<String> result = assertValid(" [ \"a\" , \"b\" ] ").get();
    assertEquals(2, result.size());
    assertEquals("a", result.get(0));
    assertEquals("b", result.get(1));
  }

  @Test
  public void testNestedArray() {
    List<List<String>> result = assertValid("[[\"a\"]]").get();
    assertEquals(1, result.size());
    assertEquals(1, result.get(0).size());
    assertEquals("a", result.get(0).get(0));
  }

  // objects

  @Test
  public void testEmptyObject() {
    Map<String, Long> result = assertValid("{}").get();
    assertTrue(result.isEmpty());
  }

  @Test
  public void testSmallObject() {
    Map<String, Long> result = assertValid("{\"a\":1}").get();
    assertEquals(1, result.size());
    assertEquals(Long.valueOf(1), result.get("a"));
  }

  @Test
  public void testBigObject() {
    Map<String, Long> result = assertValid(" { \"a\" : 1 , \"b\" : 2 } ").get();
    assertEquals(2, result.size());
    assertEquals(Long.valueOf(1), result.get("a"));
    assertEquals(Long.valueOf(2), result.get("b"));
  }

  @Test
  public void testNestedObject() {
    Map<String, Map<String, Long>> result = assertValid("{\"object\":{\"1\": 2}}").get();
    assertEquals(1, result.size());
    assertEquals(1, result.get("object").size());
    assertEquals(Long.valueOf(2), result.get("object").get("1"));
  }

  // literals

  @Test
  public void testTrue() {
    Boolean result = assertValid("true").get();
    assertTrue(result);
  }

  @Test
  public void testFalse() {
    Boolean result = assertValid("false").get();
    assertFalse(result);
  }

  @Test
  public void testNull() {
    Object result = assertValid("null").get();
    assertNull(result);
  }

  @Test
  public void testFloat() {
    assertEquals(0.0, assertValid("0.0").get(), EPSILON);
    assertEquals(0.12, assertValid("0.12").get(), EPSILON);
    assertEquals(-0.12, assertValid("-0.12").get(), EPSILON);
    assertEquals(12.34, assertValid("12.34").get(), EPSILON);
    assertEquals(-12.34, assertValid("-12.34").get(), EPSILON);
    assertEquals(1.2, assertValid("1.2e0").get(), EPSILON);
    assertEquals(1.2e-1, assertValid("1.2e-1").get(), EPSILON);
    assertEquals(1.2e-1, assertValid("1.2E-1").get(), EPSILON);
  }

  @Test
  public void testInteger() {
    assertEquals(0L, (long) assertValid("0").get());
    assertEquals(1L, (long) assertValid("1").get());
    assertEquals(-1L, (long) assertValid("-1").get());
    assertEquals(12L, (long) assertValid("12").get());
    assertEquals(-12L, (long) assertValid("-12").get());
    assertEquals(100L, (long) assertValid("1e2").get());
    assertEquals(100L, (long) assertValid("1e+2").get());
  }

  @Test
  public void testString() {
    assertEquals("", assertValid("\"\"").get());
    assertEquals("foo", assertValid("\"foo\"").get());
    assertEquals("foo bar", assertValid("\"foo bar\"").get());
  }

  @Test
  public void testStringEscaped() {
    assertEquals("\"", assertValid("\"\\\"\"").get());
    assertEquals("\\", assertValid("\"\\\\\"").get());
    assertEquals("/", assertValid("\"\\/\"").get());
    assertEquals("\b", assertValid("\"\\b\"").get());
    assertEquals("\f", assertValid("\"\\f\"").get());
    assertEquals("\n", assertValid("\"\\n\"").get());
    assertEquals("\r", assertValid("\"\\r\"").get());
    assertEquals("\t", assertValid("\"\\t\"").get());
    assertEquals("\u20Ac", assertValid("\"\\u20Ac\"").get());
  }

  @Test
  public void testExplorerEvent() {
    Result result = assertValid(
        "{\"recordset\": null, \"type\": \"change\", \"fromElement\": null, \"toElement\": null, " +
            "\"altLeft\": false, \"keyCode\": 0, \"repeat\": false, \"reason\": 0, " +
            "\"behaviorCookie\": 0, \"contentOverflow\": false, \"behaviorPart\": 0, " +
            "\"dataTransfer\": null, \"ctrlKey\": false, \"shiftLeft\": false, \"dataFld\": \"\"," +
            " \"qualifier\": \"\", \"wheelDelta\": 0, \"bookmarks\": null, \"button\": 0, " +
            "\"srcFilter\": null, \"nextPage\": \"\", \"cancelBubble\": false, \"x\": 89, \"y\": " +
            "502, \"screenX\": 231, \"screenY\": 1694, \"srcUrn\": \"\", \"boundElements\": " +
            "{\"length\": 0}, \"clientX\": 89, \"clientY\": 502, \"propertyName\": \"\", " +
            "\"shiftKey\": false, \"ctrlLeft\": false, \"offsetX\": 25, \"offsetY\": 2, " +
            "\"altKey\": false}");
    assertTrue(result.isSuccess());
  }

  @Test
  public void testFirefoxEvent() {
    Result result = assertValid(
        "{\"type\": \"change\", \"eventPhase\": 2, \"bubbles\": true, \"cancelable\": true, " +
            "\"timeStamp\": 0, \"CAPTURING_PHASE\": 1, \"AT_TARGET\": 2, \"BUBBLING_PHASE\": 3, " +
            "\"isTrusted\": true, \"MOUSEDOWN\": 1, \"MOUSEUP\": 2, \"MOUSEOVER\": 4, " +
            "\"MOUSEOUT\": 8, \"MOUSEMOVE\": 16, \"MOUSEDRAG\": 32, \"CLICK\": 64, \"DBLCLICK\": " +
            "128, \"KEYDOWN\": 256, \"KEYUP\": 512, \"KEYPRESS\": 1024, \"DRAGDROP\": 2048, " +
            "\"FOCUS\": 4096, \"BLUR\": 8192, \"SELECT\": 16384, \"CHANGE\": 32768, \"RESET\": " +
            "65536, \"SUBMIT\": 131072, \"SCROLL\": 262144, \"LOAD\": 524288, \"UNLOAD\": " +
            "1048576, \"XFER_DONE\": 2097152, \"ABORT\": 4194304, \"ERROR\": 8388608, \"LOCATE\":" +
            " 16777216, \"MOVE\": 33554432, \"RESIZE\": 67108864, \"FORWARD\": 134217728, " +
            "\"HELP\": 268435456, \"BACK\": 536870912, \"TEXT\": 1073741824, \"ALT_MASK\": 1, " +
            "\"CONTROL_MASK\": 2, \"SHIFT_MASK\": 4, \"META_MASK\": 8}");
    assertTrue(result.isSuccess());
  }

  @Test
  public void testWebkitEvent() {
    Result result = assertValid(
        "{\"returnValue\": true, \"timeStamp\": 1226697417289, \"eventPhase\": 2, \"type\": " +
            "\"change\", \"cancelable\": false, \"bubbles\": true, \"cancelBubble\": false, " +
            "\"MOUSEOUT\": 8, \"FOCUS\": 4096, \"CHANGE\": 32768, \"MOUSEMOVE\": 16, " +
            "\"AT_TARGET\": 2, \"SELECT\": 16384, \"BLUR\": 8192, \"KEYUP\": 512, \"MOUSEDOWN\": " +
            "1, \"MOUSEDRAG\": 32, \"BUBBLING_PHASE\": 3, \"MOUSEUP\": 2, \"CAPTURING_PHASE\": 1," +
            " \"MOUSEOVER\": 4, \"CLICK\": 64, \"DBLCLICK\": 128, \"KEYDOWN\": 256, \"KEYPRESS\":" +
            " 1024, \"DRAGDROP\": 2048}");
    assertTrue(result.isSuccess());
  }

  @Test
  public void testInvalidArray() {
    assertInvalid("[");
    assertInvalid("[1");
    assertInvalid("[1,");
    assertInvalid("[1,]");
    assertInvalid("[1 2]");
    assertInvalid("[]]");
  }

  @Test
  public void testInvalidObject() {
    assertInvalid("{");
    assertInvalid("{\"a\"");
    assertInvalid("{\"a\":");
    assertInvalid("{\"a\":\"b\"");
    assertInvalid("{\"a\":\"b\",");
    assertInvalid("{\"a\"}");
    assertInvalid("{\"a\":}");
    assertInvalid("{\"a\":\"b\",}");
    assertInvalid("{}}");
  }

  @Test
  public void testInvalidString() {
    assertInvalid("\"");
    assertInvalid("\"a");
    assertInvalid("\"\\\"");
    assertInvalid("\"\\a\"");
    assertInvalid("\"\\u\"");
    assertInvalid("\"\\u1\"");
    assertInvalid("\"\\u12\"");
    assertInvalid("\"\\u123\"");
    assertInvalid("\"\\u123x\"");
  }

  @Test
  public void testInvalidNumber() {
    assertInvalid("00");
    assertInvalid("01");
    assertInvalid("00.1");
  }

  @Test
  public void testInvalidTrue() {
    assertInvalid("tr");
    assertInvalid("trace");
    assertInvalid("truest");
  }

  @Test
  public void testInvalidFalse() {
    assertInvalid("fa");
    assertInvalid("falsely");
    assertInvalid("fabulous");
  }

  @Test
  public void testInvalidNull() {
    assertInvalid("nu");
    assertInvalid("nuclear");
    assertInvalid("nullified");
  }

  private Result assertValid(String input) {
    Result result = parser.parse(input);
    assertTrue(result.isSuccess());
    return result;
  }

  private Result assertInvalid(String input) {
    Result result = parser.parse(input);
    assertTrue(result.isFailure());
    return result;
  }
}


<|start_filename|>petitparser-xml/src/test/java/org/petitparser/grammar/xml/XmlEntityTest.java<|end_filename|>
package org.petitparser.grammar.xml;

import static org.junit.Assert.assertEquals;
import static org.petitparser.grammar.xml.XmlCharacterParser.encodeXmlAttributeValue;
import static org.petitparser.grammar.xml.XmlCharacterParser.encodeXmlText;

import org.junit.Test;
import org.petitparser.grammar.xml.ast.XmlDocument;
import org.petitparser.grammar.xml.ast.XmlText;
import org.petitparser.parser.Parser;

/**
 * Tests entity encoding and decoding.
 */
public class XmlEntityTest {

  private final Parser parser = new XmlParser();

  private String decode(String input) {
    XmlDocument document = parser.parse("<data>" + input + "</data>").get();
    XmlText node = (XmlText) document.getRootElement().getChildren().get(0);
    return node.getData();
  }

  @Test
  public void decodeHex() {
    assertEquals("A", decode("&#X41;"));
    assertEquals("a", decode("&#x61;"));
    assertEquals("z", decode("&#x7A;"));
  }

  @Test
  public void decodeInt() {
    assertEquals("A", decode("&#65;"));
    assertEquals("a", decode("&#97;"));
    assertEquals("z", decode("&#122;"));
  }

  @Test
  public void decodeNamed() {
    assertEquals("<", decode("&lt;"));
    assertEquals(">", decode("&gt;"));
    assertEquals("&", decode("&amp;"));
    assertEquals("'", decode("&apos;"));
    assertEquals("\"", decode("&quot;"));
  }

  @Test
  public void decodeInvalid() {
    assertEquals("&invalid;", decode("&invalid;"));
  }

  @Test
  public void decodeIncomplete() {
    assertEquals("&amp", decode("&amp"));
  }

  @Test
  public void decodeEmpty() {
    assertEquals("&;", decode("&;"));
  }

  @Test
  public void decodeSurrounded() {
    assertEquals("a&b", decode("a&amp;b"));
    assertEquals("&x&", decode("&amp;x&amp;"));
  }

  @Test
  public void decodeSequence() {
    assertEquals("&&", decode("&amp;&amp;"));
  }

  @Test
  public void encodeText() {
    assertEquals("&lt;", encodeXmlText("<"));
    assertEquals("&amp;", encodeXmlText("&"));
    assertEquals("hello", encodeXmlText("hello"));
    assertEquals("&lt;foo &amp;amp;>", encodeXmlText("<foo &amp;>"));
  }

  @Test
  public void encodeAttribute() {
    assertEquals("&quot;", encodeXmlAttributeValue("\""));
    assertEquals("hello", encodeXmlAttributeValue("hello"));
    assertEquals("&quot;hello&quot;", encodeXmlAttributeValue("\"hello\""));
  }
}


<|start_filename|>petitparser-json/src/main/java/org/petitparser/grammar/json/package-info.java<|end_filename|>
/**
 * This package contains a complete implementation of <a
 * href="http://json.org/">JSON</a>.
 * <p>
 * {@link org.petitparser.grammar.json.JsonParserDefinition} builds nested Java
 * objects from a given JSON string. Consider the following code:
 *
 * <pre>
 * Parser json = new JsonParser();
 * Object result = json.parse('{"a": 1, "b": [2, 3.4], "c": false}');
 * System.out.println(result.value);  // {a: 1, b: [2, 3.4], c: false}
 * </pre>
 * <p>
 * The grammar definition
 * {@link org.petitparser.grammar.json.JsonGrammarDefinition}
 * can be subclassed to construct other objects.
 */
package org.petitparser.grammar.json;


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlDocument.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import java.util.Collection;

/**
 * XML document node.
 */
public class XmlDocument extends XmlParent {

  public XmlDocument(Collection<XmlNode> children) {
    super(children);
  }

  @Override
  public XmlDocument getDocument() {
    return this;
  }

  public XmlElement getRootElement() {
    for (XmlNode node : getChildren()) {
      if (node instanceof XmlElement) {
        return (XmlElement) node;
      }
    }
    return null;
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/actions/ActionParser.java<|end_filename|>
package org.petitparser.parser.actions;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;
import org.petitparser.parser.combinators.DelegateParser;

import java.util.Objects;
import java.util.function.Function;

/**
 * A parser that performs a transformation with a given function on the
 * successful parse result of the delegate.
 *
 * @param <T> The type of the function argument.
 * @param <R> The type of the function result.
 */
public class ActionParser<T, R> extends DelegateParser {

  protected final Function<T, R> function;
  protected final boolean hasSideEffects;

  public ActionParser(
      Parser delegate, Function<T, R> function) {
    this(delegate, function, false);
  }

  public ActionParser(
      Parser delegate, Function<T, R> function, boolean hasSideEffects) {
    super(delegate);
    this.function = Objects.requireNonNull(function, "Undefined function");
    this.hasSideEffects = hasSideEffects;
  }

  @Override
  public Result parseOn(Context context) {
    Result result = delegate.parseOn(context);
    if (result.isSuccess()) {
      return result.success(function.apply(result.get()));
    } else {
      return result;
    }
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    // If we know to have side-effects, we have to fall back to the slow mode.
    return hasSideEffects ? super.fastParseOn(buffer, position) :
        delegate.fastParseOn(buffer, position);
  }

  @Override
  protected boolean hasEqualProperties(Parser other) {
    return super.hasEqualProperties(other) &&
        Objects.equals(function, ((ActionParser<T, R>) other).function) &&
        hasSideEffects == ((ActionParser<T, R>) other).hasSideEffects;
  }

  @Override
  public ActionParser<T, R> copy() {
    return new ActionParser<>(delegate, function, hasSideEffects);
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/context/Result.java<|end_filename|>
package org.petitparser.context;

/**
 * An immutable abstract parse result.
 */
public abstract class Result extends Context {

  public Result(String buffer, int position) {
    super(buffer, position);
  }

  /**
   * Returns {@code true} if this result indicates a parse success.
   */
  public boolean isSuccess() {
    return false;
  }

  /**
   * Returns {@code true} if this result indicates a parse failure.
   */
  public boolean isFailure() {
    return false;
  }

  /**
   * Returns the result of this parse context.
   *
   * @param <T> The type of the parse result.
   */
  public abstract <T> T get();

  /**
   * Returns the message of this parse context, only set in case of failure.
   */
  public String getMessage() {
    return null;
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/combinators/DelegateParser.java<|end_filename|>
package org.petitparser.parser.combinators;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * A parser that delegates to another one.
 */
public class DelegateParser extends Parser {

  protected Parser delegate;

  public DelegateParser(Parser delegate) {
    this.delegate =
        Objects.requireNonNull(delegate, "Undefined delegate parser");
  }

  @Override
  public Result parseOn(Context context) {
    return delegate.parseOn(context);
  }

  @Override
  public void replace(Parser source, Parser target) {
    super.replace(source, target);
    if (delegate == source) {
      delegate = target;
    }
  }

  @Override
  public List<Parser> getChildren() {
    return Collections.singletonList(delegate);
  }

  @Override
  public DelegateParser copy() {
    return new DelegateParser(delegate);
  }
}


<|start_filename|>petitparser-core/src/test/java/org/petitparser/utils/TracerTest.java<|end_filename|>
package org.petitparser.utils;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.petitparser.ExamplesTest;
import org.petitparser.context.Result;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Tests {@link org.petitparser.utils.Tracer}.
 */
public class TracerTest {

  @Test
  public void testSuccessfulTrace() {
    List<String> expected = Arrays.asList(
        "FlattenParser",
        "  SequenceParser",
        "    CharacterParser[letter expected]",
        "    Success[1:2]: a",
        "    PossessiveRepeatingParser[0..*]",
        "      CharacterParser[letter or digit expected]",
        "      Failure[1:2]: letter or digit expected",
        "    Success[1:2]: []",
        "  Success[1:2]: [a, []]",
        "Success[1:2]: a"
    );
    List<Tracer.TraceEvent> actual = new ArrayList<>();
    Result result = Tracer.on(ExamplesTest.IDENTIFIER, actual::add).parse("a");
    assertTrue(result.isSuccess());
    assertEquals(expected, actual.stream().map(Tracer.TraceEvent::toString)
        .collect(Collectors.toList()));
  }

  @Test
  public void testFailingTrace() {
    List<String> expected = Arrays.asList(
        "FlattenParser",
        "  SequenceParser",
        "    CharacterParser[letter expected]",
        "    Failure[1:1]: letter expected",
        "  Failure[1:1]: letter expected",
        "Failure[1:1]: letter expected"
    );
    List<Tracer.TraceEvent> actual = new ArrayList<>();
    Result result = Tracer.on(ExamplesTest.IDENTIFIER, actual::add).parse("1");
    assertFalse(result.isSuccess());
    assertEquals(expected, actual.stream().map(Tracer.TraceEvent::toString)
        .collect(Collectors.toList()));
  }

}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/primitive/CharacterPredicate.java<|end_filename|>
package org.petitparser.parser.primitive;

import org.petitparser.parser.Parser;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Character predicate.
 */
@FunctionalInterface
public interface CharacterPredicate {

  /**
   * Returns a character predicate that matches any character.
   */
  static CharacterPredicate any() {
    return value -> true;
  }

  /**
   * Returns a character predicate that matches any of the characters in {@code
   * string}.
   */
  static CharacterPredicate anyOf(String string) {
    List<CharacterRange> ranges = string.chars()
        .mapToObj(value -> new CharacterRange((char) value, (char) value))
        .collect(Collectors.toList());
    return CharacterRange.toCharacterPredicate(ranges);
  }

  /**
   * Returns a character predicate that matches no character.
   */
  static CharacterPredicate none() {
    return value -> false;
  }

  /**
   * Returns a character predicate that matches none of the characters in {@code
   * string}.
   */
  static CharacterPredicate noneOf(String string) {
    List<CharacterRange> ranges = string.chars()
        .mapToObj(value -> new CharacterRange((char) value, (char) value))
        .collect(Collectors.toList());
    return CharacterRange.toCharacterPredicate(ranges).not();
  }

  /**
   * Returns a character predicate that matches the given {@code character}.
   */
  static CharacterPredicate of(char character) {
    return value -> value == character;
  }

  /**
   * Returns a character predicate that matches any character between {@code
   * start} and {@code stop}.
   */
  static CharacterPredicate range(char start, char stop) {
    return value -> start <= value && value <= stop;
  }

  /**
   * Returns a character predicate that matches character ranges between {@code
   * starts} and {@code stops}.
   */
  static CharacterPredicate ranges(char[] starts, char[] stops) {
    if (starts.length != stops.length) {
      throw new IllegalArgumentException("Invalid range sizes.");
    }
    for (int i = 0; i < starts.length; i++) {
      if (starts[i] > stops[i]) {
        throw new IllegalArgumentException(
            "Invalid range: " + starts[i] + "-" + stops[i]);
      }
      if (i + 1 < starts.length && starts[i + 1] <= stops[i]) {
        throw new IllegalArgumentException("Invalid sequence.");
      }
    }
    return value -> {
      int index = Arrays.binarySearch(starts, value);
      return index >= 0 || index < -1 && value <= stops[-index - 2];
    };
  }

  /**
   * Returns a character predicate that matches the provided pattern.
   */
  static CharacterPredicate pattern(String pattern) {
    return PatternParser.PATTERN.parse(pattern).get();
  }

  class PatternParser {
    static final Parser PATTERN_SIMPLE = CharacterParser.any()
        .map((Character value) -> new CharacterRange(value, value));
    static final Parser PATTERN_RANGE =
        CharacterParser.any().seq(CharacterParser.of('-'))
            .seq(CharacterParser.any()).map(
            (List<Character> values) -> new CharacterRange(values.get(0),
                values.get(2)));
    static final Parser PATTERN_POSITIVE =
        PATTERN_RANGE.or(PATTERN_SIMPLE).star()
            .map(CharacterRange::toCharacterPredicate);
    static final Parser PATTERN =
        CharacterParser.of('^').optional().seq(PATTERN_POSITIVE)
            .map((List<CharacterPredicate> predicate) -> {
              return predicate.get(0) == null ? predicate.get(1) :
                  predicate.get(1).not();
            }).end();
  }

  /**
   * Tests if the character predicate is satisfied.
   */
  boolean test(char value);

  /**
   * Negates this character predicate.
   */
  default CharacterPredicate not() {
    return new NotCharacterPredicate(this);
  }

  /**
   * The negated character predicate.
   */
  class NotCharacterPredicate implements CharacterPredicate {

    private final CharacterPredicate predicate;

    public NotCharacterPredicate(CharacterPredicate predicate) {
      this.predicate = predicate;
    }

    @Override
    public boolean test(char value) {
      return !predicate.test(value);
    }

    @Override
    public CharacterPredicate not() {
      return predicate;
    }
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlDoctype.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import org.petitparser.grammar.xml.XmlDefinition;

/**
 * XML doctype node.
 */
public class XmlDoctype extends XmlData {

  public XmlDoctype(String data) {
    super(data);
  }

  @Override
  public void writeTo(StringBuilder buffer) {
    buffer.append(XmlDefinition.OPEN_DOCTYPE);
    buffer.append(XmlDefinition.WHITESPACE);
    buffer.append(getData());
    buffer.append(XmlDefinition.CLOSE_DOCTYPE);
  }
}


<|start_filename|>petitparser-xml/src/main/java/org/petitparser/grammar/xml/ast/XmlNode.java<|end_filename|>
package org.petitparser.grammar.xml.ast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Abstract XML node.
 */
public abstract class XmlNode implements Iterable<XmlNode> {

  private XmlNode parent;

  /**
   * Answer the parent node of the receiver, or {@code null} if there is none.
   */
  public XmlNode getParent() {
    return parent;
  }

  /**
   * Set the parent of the receiver.
   */
  void setParent(XmlNode parent) {
    this.parent = parent;
  }

  /**
   * Answer the attribute nodes of the receiver.
   */
  public List<XmlAttribute> getAttributes() {
    return Collections.emptyList();
  }

  /**
   * Answer the child nodes of the receiver.
   */
  public List<XmlNode> getChildren() {
    return Collections.emptyList();
  }

  /**
   * Answer an iterator over the receiver, all attributes and nested children.
   */
  @Override
  public Iterator<XmlNode> iterator() {
    List<XmlNode> nodes = new ArrayList<>();
    allAllNodesTo(nodes);
    return nodes.iterator();
  }

  private void allAllNodesTo(List<XmlNode> nodes) {
    nodes.add(this);
    nodes.addAll(getAttributes());
    for (XmlNode node : getChildren()) {
      node.allAllNodesTo(nodes);
    }
  }

  /**
   * Answer the root of the subtree in which this node is found, whether that's
   * a document or another element.
   */
  public XmlNode getRoot() {
    return parent == null ? this : parent.getRoot();
  }

  /**
   * Answer the document that contains this node, or {@code null} if the node is
   * not part of a document.
   */
  public XmlDocument getDocument() {
    return parent == null ? null : parent.getDocument();
  }

  /**
   * Answer the first child of the receiver or {@code null}.
   */
  public final XmlNode getFirstChild() {
    List<XmlNode> children = getChildren();
    return children.isEmpty() ? null : children.get(0);
  }

  /**
   * Answer the last child of the receiver or {@code null}.
   */
  public final XmlNode getLastChild() {
    List<XmlNode> children = getChildren();
    return children.isEmpty() ? null : children.get(children.size() - 1);
  }

  /**
   * Answer the next sibling of the receiver or {@code null}.
   */
  public final XmlNode getNextSibling() {
    XmlNode parent = getParent();
    if (parent == null) {
      return null;
    }
    List<XmlNode> children = parent.getChildren();
    for (int i = 0; i < children.size() - 1; i++) {
      if (children.get(i) == this) {
        return children.get(i + 1);
      }
    }
    return null;
  }

  /**
   * Answer the previous sibling of the receiver or {@code null}.
   */
  public final XmlNode getPreviousSibling() {
    XmlNode parent = getParent();
    if (parent == null) {
      return null;
    }
    List<XmlNode> children = parent.getChildren();
    for (int i = 1; i < children.size(); i++) {
      if (children.get(i) == this) {
        return children.get(i - 1);
      }
    }
    return null;
  }

  /**
   * Answer a print string of the receiver.
   */
  @Override
  public String toString() {
    return super.toString() + " (" + toXmlString() + ")";
  }

  /**
   * Answer an XML string of the receiver.
   */
  public String toXmlString() {
    StringBuilder buffer = new StringBuilder();
    writeTo(buffer);
    return buffer.toString();
  }

  /**
   * Writes the XML string of the receiver to a {@code buffer}.
   */
  public abstract void writeTo(StringBuilder buffer);
}


<|start_filename|>petitparser-xml/src/test/java/org/petitparser/grammar/xml/XmlBenchmark.java<|end_filename|>
package org.petitparser.grammar.xml;

import org.petitparser.parser.Parser;

/**
 * Test the performance of {@link XmlParser}.
 */
public class XmlBenchmark {

  private static final Parser PARSER = new XmlParser();
  private static final String INPUT =
        "<?xml version=\"1.0\"?>"
      + "<catalog>"
      + "   <book id=\"bk101\">"
      + "      <author><NAME></author>"
      + "      <title>XML Developer's Guide</title>"
      + "      <genre>Computer</genre>"
      + "      <price>44.95</price>"
      + "      <publish_date>2000-10-01</publish_date>"
      + "      <description>An in-depth look at creating applications "
      + "      with XML.</description>"
      + "   </book>"
      + "   <book id=\"bk102\">"
      + "      <author><NAME></author>"
      + "      <title>Midnight Rain</title>"
      + "      <genre>Fantasy</genre>"
      + "      <price>5.95</price>"
      + "      <publish_date>2000-12-16</publish_date>"
      + "      <description>A former architect battles corporate zombies, "
      + "      an evil sorceress, and her own childhood to become queen "
      + "      of the world.</description>"
      + "   </book>"
      + "   <book id=\"bk103\">"
      + "      <author><NAME></author>"
      + "      <title>Maeve Ascendant</title>"
      + "      <genre>Fantasy</genre>"
      + "      <price>5.95</price>"
      + "      <publish_date>2000-11-17</publish_date>"
      + "      <description>After the collapse of a nanotechnology "
      + "      society in England, the young survivors lay the "
      + "      foundation for a new society.</description>"
      + "   </book>"
      + "   <book id=\"bk104\">"
      + "      <author><NAME></author>"
      + "      <title>Oberon's Legacy</title>"
      + "      <genre>Fantasy</genre>"
      + "      <price>5.95</price>"
      + "      <publish_date>2001-03-10</publish_date>"
      + "      <description>In post-apocalypse England, the mysterious "
      + "      agent known only as Oberon helps to create a new life "
      + "      for the inhabitants of London. Sequel to Maeve "
      + "      Ascendant.</description>"
      + "   </book>"
      + "   <book id=\"bk105\">"
      + "      <author><NAME></author>"
      + "      <title>The Sundered Grail</title>"
      + "      <genre>Fantasy</genre>"
      + "      <price>5.95</price>"
      + "      <publish_date>2001-09-10</publish_date>"
      + "      <description>The two daughters of Maeve, half-sisters, "
      + "      battle one another for control of England. Sequel to "
      + "      Oberon's Legacy.</description>"
      + "   </book>"
      + "   <book id=\"bk106\">"
      + "      <author><NAME></author>"
      + "      <title>Lover Birds</title>"
      + "      <genre>Romance</genre>"
      + "      <price>4.95</price>"
      + "      <publish_date>2000-09-02</publish_date>"
      + "      <description>When Carla meets Paul at an ornithology "
      + "      conference, tempers fly as feathers get ruffled.</description>"
      + "   </book>"
      + "   <book id=\"bk107\">"
      + "      <author><NAME></author>"
      + "      <title>Splish Splash</title>"
      + "      <genre>Romance</genre>"
      + "      <price>4.95</price>"
      + "      <publish_date>2000-11-02</publish_date>"
      + "      <description>A deep sea diver finds true love twenty "
      + "      thousand leagues beneath the sea.</description>"
      + "   </book>"
      + "   <book id=\"bk108\">"
      + "      <author><NAME></author>"
      + "      <title>Creepy Crawlies</title>"
      + "      <genre>Horror</genre>"
      + "      <price>4.95</price>"
      + "      <publish_date>2000-12-06</publish_date>"
      + "      <description>An anthology of horror stories about roaches,"
      + "      centipedes, scorpions  and other insects.</description>"
      + "   </book>"
      + "   <book id=\"bk109\">"
      + "      <author><NAME></author>"
      + "      <title>Paradox Lost</title>"
      + "      <genre>Science Fiction</genre>"
      + "      <price>6.95</price>"
      + "      <publish_date>2000-11-02</publish_date>"
      + "      <description>After an inadvertant trip through a Heisenberg"
      + "      Uncertainty Device, <NAME> discovers the problems "
      + "      of being quantum.</description>"
      + "   </book>"
      + "   <book id=\"bk110\">"
      + "      <author><NAME></author>"
      + "      <title>Microsoft .NET: The Programming Bible</title>"
      + "      <genre>Computer</genre>"
      + "      <price>36.95</price>"
      + "      <publish_date>2000-12-09</publish_date>"
      + "      <description>Microsoft's .NET initiative is explored in "
      + "      detail in this deep programmer's reference.</description>"
      + "   </book>"
      + "   <book id=\"bk111\">"
      + "      <author><NAME></author>"
      + "      <title>MSXML3: A Comprehensive Guide</title>"
      + "      <genre>Computer</genre>"
      + "      <price>36.95</price>"
      + "      <publish_date>2000-12-01</publish_date>"
      + "      <description>The Microsoft MSXML3 parser is covered in "
      + "      detail, with attention to XML DOM interfaces, XSLT processing, "
      + "      SAX and more.</description>"
      + "   </book>"
      + "   <book id=\"bk112\">"
      + "      <author><NAME></author>"
      + "      <title>Visual Studio 7: A Comprehensive Guide</title>"
      + "      <genre>Computer</genre>"
      + "      <price>49.95</price>"
      + "      <publish_date>2001-04-16</publish_date>"
      + "      <description>Microsoft Visual Studio 7 is explored in depth,"
      + "      looking at how Visual Basic, Visual C++, C#, and ASP+ are "
      + "      integrated into a comprehensive development "
      + "      environment.</description>"
      + "   </book>"
      + "</catalog>";

  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 2000; i++) {
      PARSER.parse(INPUT).get();
    }
    long stop = System.currentTimeMillis();
    System.out.println(Double.toString((stop - start) / 1000.0) + "s");
  }

}


<|start_filename|>petitparser-core/src/test/java/org/petitparser/RegressionsTest.java<|end_filename|>
package org.petitparser;

import static org.petitparser.Assertions.assertSuccess;
import static org.petitparser.parser.primitive.CharacterParser.of;

import org.junit.Test;
import org.petitparser.parser.Parser;

/**
 * Various regressions amd tricky examples.
 */
public class RegressionsTest {

  @Test
  public void testFlattenTrim() {
    Parser parser = of('a').flatten().trim();
    assertSuccess(parser, "a", "a");
    assertSuccess(parser, " a ", "a");
    assertSuccess(parser, "  a  ", "a");
  }

  @Test
  public void testTrimFlatten() {
    Parser parser = of('a').trim().flatten();
    assertSuccess(parser, "a", "a");
    assertSuccess(parser, " a ", " a ");
    assertSuccess(parser, "  a  ", "  a  ");
  }
}


<|start_filename|>petitparser-core/src/main/java/org/petitparser/parser/combinators/NotParser.java<|end_filename|>
package org.petitparser.parser.combinators;

import org.petitparser.context.Context;
import org.petitparser.context.Result;
import org.petitparser.parser.Parser;

import java.util.Objects;

/**
 * The not-predicate, a parser that succeeds whenever its delegate does not, but
 * consumes no input [Parr 1994, 1995].
 */
public class NotParser extends DelegateParser {

  protected final String message;

  public NotParser(Parser delegate, String message) {
    super(delegate);
    this.message = Objects.requireNonNull(message, "Undefined message");
  }

  @Override
  public Result parseOn(Context context) {
    Result result = delegate.parseOn(context);
    if (result.isFailure()) {
      return context.success(null);
    } else {
      return context.failure(message);
    }
  }

  @Override
  public int fastParseOn(String buffer, int position) {
    int result = delegate.fastParseOn(buffer, position);
    return result < 0 ? position : -1;
  }

  @Override
  protected boolean hasEqualProperties(Parser other) {
    return super.hasEqualProperties(other) &&
        Objects.equals(message, ((NotParser) other).message);
  }

  @Override
  public NotParser copy() {
    return new NotParser(delegate, message);
  }

  @Override
  public String toString() {
    return super.toString() + "[" + message + "]";
  }
}
