Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into dependabot/maven/mi…
Browse files Browse the repository at this point in the history
…nor-updates-64626ea61c
  • Loading branch information
mvanaken committed Apr 12, 2024
2 parents 21cfbca + f19ff79 commit c110275
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 95 deletions.
35 changes: 23 additions & 12 deletions core/src/main/java/io/parsingdata/metal/data/ParseGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class ParseGraph extends ImmutableObject implements ParseItem {
public final ParseGraph tail;
public final boolean branched;
public final Token definition;
public final int scopeDepth;
public final long size;

public static final Token NONE = new Token("NONE", null) {
Expand All @@ -47,52 +48,61 @@ public class ParseGraph extends ImmutableObject implements ParseItem {
private ParseGraph(final Token definition) {
head = null;
tail = null;
branched = false;
this.definition = checkNotNull(definition, "definition");
branched = false;
scopeDepth = 0;
size = 0;
}

private ParseGraph(final ParseItem head, final ParseGraph tail, final Token definition, final boolean branched) {
private ParseGraph(final ParseItem head, final ParseGraph tail, final Token definition, final boolean branched, final int scopeDepth) {
this.head = checkNotNull(head, "head");
this.tail = checkNotNull(tail, "tail");
this.branched = branched;
this.definition = checkNotNull(definition, "definition");
this.branched = branched;
this.scopeDepth = scopeDepth;
size = tail.size + 1;
}

private ParseGraph(final ParseItem head, final ParseGraph tail, final Token definition) {
this(head, tail, definition, false);
this(head, tail, definition, false, 0);
}

protected ParseGraph add(final ParseValue head) {
if (branched) {
return new ParseGraph(this.head.asGraph().add(head), tail, definition, true);
return new ParseGraph(this.head.asGraph().add(head), tail, definition, true, scopeDepth);
}
return new ParseGraph(head, this, definition);
}

protected ParseGraph add(final ParseReference parseReference) {
if (branched) {
return new ParseGraph(head.asGraph().add(parseReference), tail, definition, true);
return new ParseGraph(head.asGraph().add(parseReference), tail, definition, true, scopeDepth);
}
return new ParseGraph(parseReference, this, definition);
}

protected ParseGraph addBranch(final Token definition) {
if (branched) {
return new ParseGraph(head.asGraph().addBranch(definition), tail, this.definition, true);
return new ParseGraph(head.asGraph().addBranch(definition), tail, this.definition, true, definition.isScopeDelimiter() ? scopeDepth + 1 : scopeDepth);
}
return new ParseGraph(new ParseGraph(definition), this, this.definition, true);
return new ParseGraph(new ParseGraph(definition), this, this.definition, true, definition.isScopeDelimiter() ? 1 : 0);
}

protected ParseGraph closeBranch() {
protected ParseGraph closeBranch(final Token token) {
if (!branched) {
throw new IllegalStateException("Cannot close branch that is not open.");
}
final int newScopeDepth = token.isScopeDelimiter() ? scopeDepth - 1 : scopeDepth;
if (head.asGraph().branched) {
return new ParseGraph(head.asGraph().closeBranch(), tail, definition, true);
return new ParseGraph(head.asGraph().closeBranch(token), tail, definition, true, newScopeDepth);
}
if (!head.getDefinition().equals(token)) {
throw new IllegalStateException("Cannot close branch with token that does not match its head token.");
}
if (newScopeDepth != 0) {
throw new IllegalStateException("Cannot close parse graph that has a non zero scopeDepth.");
}
return new ParseGraph(head, tail, definition, false);
return new ParseGraph(head, tail, definition);
}

public boolean isEmpty() { return size == 0; }
Expand Down Expand Up @@ -131,7 +141,7 @@ public String toString() {
if (isEmpty()) {
return "pg(terminator:" + definition.getClass().getSimpleName() + ")";
}
return "pg(" + head + "," + tail + "," + branched + ")";
return "pg(" + head + "," + tail + "," + branched + "," + scopeDepth + ")";
}

@Override
Expand All @@ -140,6 +150,7 @@ public boolean equals(final Object obj) {
&& Objects.equals(head, ((ParseGraph)obj).head)
&& Objects.equals(tail, ((ParseGraph)obj).tail)
&& Objects.equals(branched, ((ParseGraph)obj).branched)
&& Objects.equals(scopeDepth, ((ParseGraph)obj).scopeDepth)
&& Objects.equals(definition, ((ParseGraph)obj).definition);
// The size field is excluded from equals() and hashCode() because it is cached data.
}
Expand Down
31 changes: 14 additions & 17 deletions core/src/main/java/io/parsingdata/metal/data/ParseState.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,63 +42,61 @@ public class ParseState extends ImmutableObject {
public final Source source;
public final ImmutableList<ImmutablePair<Token, BigInteger>> iterations;
public final ImmutableList<ParseReference> references;
public final int scopeDepth;

public ParseState(final ParseGraph order, final ParseValueCache cache, final Source source, final BigInteger offset, final ImmutableList<ImmutablePair<Token, BigInteger>> iterations, final ImmutableList<ParseReference> references, final int scopeDepth) {
public ParseState(final ParseGraph order, final ParseValueCache cache, final Source source, final BigInteger offset, final ImmutableList<ImmutablePair<Token, BigInteger>> iterations, final ImmutableList<ParseReference> references) {
this.order = checkNotNull(order, "order");
this.cache = checkNotNull(cache, "cache");
this.source = checkNotNull(source, "source");
this.offset = checkNotNegative(offset, "offset");
this.iterations = checkNotNull(iterations, "iterations");
this.references = checkNotNull(references, "references");
this.scopeDepth = scopeDepth;
}

public static ParseState createFromByteStream(final ByteStream input, final BigInteger offset) {
return new ParseState(ParseGraph.EMPTY, new ParseValueCache(), new ByteStreamSource(input), offset, new ImmutableList<>(), new ImmutableList<>(), 0);
return new ParseState(ParseGraph.EMPTY, new ParseValueCache(), new ByteStreamSource(input), offset, new ImmutableList<>(), new ImmutableList<>());
}

public static ParseState createFromByteStream(final ByteStream input) {
return createFromByteStream(input, ZERO);
}

public ParseState addBranch(final Token token) {
return new ParseState(order.addBranch(token), cache, source, offset, token.isIterable() ? iterations.add(new ImmutablePair<>(token, ZERO)) : iterations, references, token.isScopeDelimiter() ? scopeDepth + 1 : scopeDepth);
return new ParseState(order.addBranch(token), cache, source, offset, token.isIterable() ? iterations.add(new ImmutablePair<>(token, ZERO)) : iterations, references);
}

public ParseState closeBranch(final Token token) {
if (token.isIterable() && !iterations.head.left.equals(token)) {
throw new IllegalStateException(format("Cannot close branch for iterable token %s. Current iteration state is for token %s.", token.name, iterations.head.left.name));
}
return new ParseState(order.closeBranch(), cache, source, offset, token.isIterable() ? iterations.tail : iterations, references, token.isScopeDelimiter() ? scopeDepth - 1 : scopeDepth);
return new ParseState(order.closeBranch(token), cache, source, offset, token.isIterable() ? iterations.tail : iterations, references);
}

public ParseState add(final ParseReference parseReference) {
return new ParseState(order, cache, source, offset, iterations, references.add(parseReference), scopeDepth);
return new ParseState(order, cache, source, offset, iterations, references.add(parseReference));
}

public ParseState add(final ParseValue parseValue) {
return new ParseState(order.add(parseValue), cache.add(parseValue), source, offset, iterations, references, scopeDepth);
return new ParseState(order.add(parseValue), cache.add(parseValue), source, offset, iterations, references);
}

public ParseState createCycle(final ParseReference parseReference) {
return new ParseState(order.add(parseReference), cache, source, offset, iterations, references, scopeDepth);
return new ParseState(order.add(parseReference), cache, source, offset, iterations, references);
}

public ParseState iterate() {
return new ParseState(order, cache, source, offset, iterations.tail.add(new ImmutablePair<>(iterations.head.left, iterations.head.right.add(ONE))), references, scopeDepth);
return new ParseState(order, cache, source, offset, iterations.tail.add(new ImmutablePair<>(iterations.head.left, iterations.head.right.add(ONE))), references);
}

public Optional<ParseState> seek(final BigInteger newOffset) {
return newOffset.compareTo(ZERO) >= 0 ? Optional.of(new ParseState(order, cache, source, newOffset, iterations, references, scopeDepth)) : Optional.empty();
return newOffset.compareTo(ZERO) >= 0 ? Optional.of(new ParseState(order, cache, source, newOffset, iterations, references)) : Optional.empty();
}

public ParseState withOrder(final ParseGraph order) {
return new ParseState(order, NO_CACHE, source, offset, iterations, references, scopeDepth);
return new ParseState(order, NO_CACHE, source, offset, iterations, references);
}

public ParseState withSource(final Source source) {
return new ParseState(order, cache, source, ZERO, iterations, references, scopeDepth);
return new ParseState(order, cache, source, ZERO, iterations, references);
}

public Optional<Slice> slice(final BigInteger length) {
Expand All @@ -109,7 +107,7 @@ public Optional<Slice> slice(final BigInteger length) {
public String toString() {
final String iterationsString = iterations.isEmpty() ? "" : ";iterations:" + iterations;
final String referencesString = references.isEmpty() ? "" : ";references:" + references;
return getClass().getSimpleName() + "(source:" + source + ";offset:" + offset + ";order:" + order + iterationsString + referencesString + ";scopeDepth:" + scopeDepth + ";" + cache + ")";
return getClass().getSimpleName() + "(source:" + source + ";offset:" + offset + ";order:" + order + iterationsString + referencesString + ";" + cache + ")";
}

@Override
Expand All @@ -120,13 +118,12 @@ public boolean equals(final Object obj) {
&& Objects.equals(offset, ((ParseState)obj).offset)
&& Objects.equals(source, ((ParseState)obj).source)
&& Objects.equals(iterations, ((ParseState)obj).iterations)
&& Objects.equals(references, ((ParseState)obj).references)
&& Objects.equals(scopeDepth, ((ParseState)obj).scopeDepth);
&& Objects.equals(references, ((ParseState)obj).references);
}

@Override
public int immutableHashCode() {
return Objects.hash(getClass(), order, cache, offset, source, iterations, references, scopeDepth);
return Objects.hash(getClass(), order, cache, offset, source, iterations, references);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public static class NameRef extends Ref<String> {
protected ImmutableList<Value> evalImpl(final ParseState parseState, final int limit, final int requestedScope) {
return Optional.of(parseState.cache)
.filter(p -> references.size == 1)
.filter(p -> requestedScope >= parseState.scopeDepth)
.filter(p -> requestedScope >= parseState.order.scopeDepth)
.flatMap(p -> p.find(references.head, limit))
.orElseGet(() -> super.evalImpl(parseState, limit, requestedScope));
}
Expand Down Expand Up @@ -124,7 +124,7 @@ public DefinitionRef withScope(final SingleValueExpression scope) {

@Override
public ImmutableList<Value> eval(final ParseState parseState, final Encoding encoding) {
final int requestedScope = scope == null ? parseState.scopeDepth : scope.evalSingle(parseState, encoding)
final int requestedScope = scope == null ? parseState.order.scopeDepth : scope.evalSingle(parseState, encoding)
.filter(sizeValue -> !sizeValue.equals(NOT_A_VALUE) && sizeValue.asNumeric().compareTo(ZERO) >= 0)
.orElseThrow(() -> new IllegalArgumentException("Argument scopeSize must evaluate to a positive, countable value.")).asNumeric().intValueExact();
if (limit == null) {
Expand All @@ -136,7 +136,7 @@ public ImmutableList<Value> eval(final ParseState parseState, final Encoding enc
}

protected ImmutableList<Value> evalImpl(final ParseState parseState, final int limit, final int requestedScope) {
return wrap(getAllValues(parseState.order, parseValue -> toList(references).stream().anyMatch(ref -> predicate.test(parseValue, ref)), limit, requestedScope, parseState.scopeDepth), new ImmutableList<Value>()).computeResult();
return wrap(getAllValues(parseState.order, parseValue -> toList(references).stream().anyMatch(ref -> predicate.test(parseValue, ref)), limit, requestedScope, parseState.order.scopeDepth), new ImmutableList<Value>()).computeResult();
}

static <T> List<T> toList(final ImmutableList<T> allValues) {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/io/parsingdata/metal/token/Tie.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ protected Optional<ParseState> parseImpl(final Environment environment) {

private Trampoline<Optional<ParseState>> iterate(final Environment environment, final ImmutableList<Value> values, final int index, final ParseState returnParseState) {
if (values.isEmpty()) {
return complete(() -> success(new ParseState(environment.parseState.closeBranch(this).order, environment.parseState.cache, returnParseState.source, returnParseState.offset, returnParseState.iterations, returnParseState.references, returnParseState.scopeDepth)));
return complete(() -> success(new ParseState(environment.parseState.closeBranch(this).order, environment.parseState.cache, returnParseState.source, returnParseState.offset, returnParseState.iterations, returnParseState.references)));
}
if (values.head.equals(NOT_A_VALUE)) {
return complete(Util::failure);
Expand Down
14 changes: 10 additions & 4 deletions core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;

import static io.parsingdata.metal.Shorthand.TRUE;
import static io.parsingdata.metal.Shorthand.con;
Expand Down Expand Up @@ -68,6 +66,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
Expand Down Expand Up @@ -220,7 +219,7 @@ public class AutoEqualityTest {
private static final List<Supplier<Object>> PARSE_ITEMS = List.of(() -> CLOSED_BRANCHED_GRAPH, () -> ParseGraph.EMPTY, () -> GRAPH_WITH_REFERENCE, () -> createFromByteStream(DUMMY_STREAM).add(PARSE_VALUE).order, () -> createFromByteStream(DUMMY_STREAM).add(PARSE_VALUE).add(PARSE_VALUE).order, () -> BRANCHED_GRAPH);
private static final List<Supplier<Object>> BYTE_STREAMS = List.of(() -> new InMemoryByteStream(new byte[] { 1, 2 }), () -> DUMMY_STREAM);
private static final List<Supplier<Object>> BIG_INTEGERS = List.of(() -> ONE, () -> BigInteger.valueOf(3));
private static final List<Supplier<Object>> PARSE_STATES = List.of(() -> createFromByteStream(DUMMY_STREAM), () -> createFromByteStream(DUMMY_STREAM, ONE), () -> new ParseState(GRAPH_WITH_REFERENCE, NO_CACHE, DUMMY_BYTE_STREAM_SOURCE, TEN, new ImmutableList<>(), new ImmutableList<>(), 0));
private static final List<Supplier<Object>> PARSE_STATES = List.of(() -> createFromByteStream(DUMMY_STREAM), () -> createFromByteStream(DUMMY_STREAM, ONE), () -> new ParseState(GRAPH_WITH_REFERENCE, NO_CACHE, DUMMY_BYTE_STREAM_SOURCE, TEN, new ImmutableList<>(), new ImmutableList<>()));
private static final List<Supplier<Object>> PARSE_VALUE_CACHES = List.of(() -> NO_CACHE, ParseValueCache::new, () -> new ParseValueCache().add(PARSE_VALUE), () -> new ParseValueCache().add(PARSE_VALUE).add(PARSE_VALUE));
private static final List<Supplier<Object>> IMMUTABLE_LISTS = List.of(ImmutableList::new, () -> ImmutableList.create("TEST"), () -> ImmutableList.create(1), () -> ImmutableList.create(1).add(2));
private static final List<Supplier<Object>> BOOLEANS = List.of(() -> true, () -> false);
Expand Down Expand Up @@ -262,7 +261,7 @@ private static Map<Class<?>, List<Supplier<Object>>> buildMap() {
public static Stream<Arguments> data() throws IllegalAccessException, InvocationTargetException, InstantiationException {
final Set<Class<?>> classes = findClasses().filter(not(CLASSES_TO_IGNORE::contains)).collect(toSet());
classes.removeAll(CLASSES_TO_TEST);
assertEquals(Set.of(), classes, "Please add missing class to the CLASSES_TO_TEST or CLASSES_TO_IGNORE constant.");
Assertions.assertEquals(Set.of(), classes, "Please add missing class to the CLASSES_TO_TEST or CLASSES_TO_IGNORE constant.");
return generateObjectArrays(CLASSES_TO_TEST).stream();
}

Expand Down Expand Up @@ -437,4 +436,11 @@ public void basicNoHashCollisions(final Object object, final Object same, final
}
}

private static void assertEquals(final Object o, final Object object) {
Assertions.assertEquals(o, object, String.format("Objects should be equal:\n%s\n%s\n", o, object));
}
private static void assertNotEquals(final Object o, final Object object) {
Assertions.assertNotEquals(o, object, String.format("Objects should not be equal:\n%s\n%s\n", o, object));
}

}
Loading

0 comments on commit c110275

Please sign in to comment.