Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Current iteration #242

Merged
merged 26 commits into from
Sep 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e725c68
#236: Basic, compiling and failing tests for the CurrentIndex ValueEx…
jvdb Jul 2, 2018
e6b2f37
#236: Added test for another Token that needs to be included: While.
jvdb Jul 3, 2018
0e12551
#236: Basic implementation and tests.
jvdb Jul 4, 2018
dac4afb
#236: Added (failing) test for using CurrentIndex in a nested iterable.
jvdb Jul 4, 2018
b449cff
#236: Improved currentIndex implementation by looking for the deepest…
mvanaken Jul 17, 2018
a294916
#236: Renamed CurrentIndex to CurrentIteration to better reflect what…
jvdb Jul 18, 2018
ec291c2
#236: Fixed one of the CurrentIteration tests.
jvdb Jul 18, 2018
c3c961e
#236: Simplified implementation of CurrentIteration, which also impro…
jvdb Jul 18, 2018
cab9c3e
#236: Improved test coverage for CurrentIteration.
jvdb Jul 18, 2018
3450821
#236: Converted CurrentIteration to use trampolines for recursion.
jvdb Jul 18, 2018
0d2401b
#236: Added unit test for CurrentIteration.
jvdb Jul 18, 2018
ad98634
#236: Keep track of the last iterable instead of the last iteration a…
mvanaken Jul 20, 2018
f8f5034
#236: Small refactorings.
jvdb Jul 21, 2018
a17967c
#236: Make CurrentIteration return empty value when referencing outsi…
mvanaken Jul 24, 2018
b2961aa
#236: More tests. Unfortunately, no way to more precisely check for e…
jvdb Jul 31, 2018
6fa43c5
#236: New implementation for current iteration, which is now tracked …
mvanaken Aug 20, 2018
ebfcf71
#236: Removed redundant import.
mvanaken Aug 20, 2018
6b72849
#236: Processed review comments of @rdvdijk.
mvanaken Aug 24, 2018
fe71ac2
#236: Included ToString test for ParseState with iterations.
mvanaken Aug 24, 2018
8f9d2c3
#236: Processed review comments from @rdvdijk @ccreeten.
mvanaken Sep 3, 2018
97a66c5
#236: Fixed a failing test.
jvdb Sep 3, 2018
5442e7a
#236: Added test to repair test coverage.
jvdb Sep 10, 2018
93716c3
236: Improved CurrentIterationTest.
jvdb Sep 15, 2018
fd803d8
236: Improved CurrentIterationTest to actually verify zero-sized Def …
jvdb Sep 15, 2018
c63f84c
#236: Replaced null-check with isEmpty()-call, added newline, improve…
jvdb Sep 16, 2018
840c354
#236: Removed dependency on the javafx Pair class by adding trivial I…
jvdb Sep 16, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion core/src/main/java/io/parsingdata/metal/Shorthand.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@
import io.parsingdata.metal.expression.value.ConstantFactory;
import io.parsingdata.metal.expression.value.Elvis;
import io.parsingdata.metal.expression.value.Expand;
import io.parsingdata.metal.expression.value.FoldCat;
import io.parsingdata.metal.expression.value.FoldLeft;
import io.parsingdata.metal.expression.value.FoldRight;
import io.parsingdata.metal.expression.value.FoldCat;
import io.parsingdata.metal.expression.value.Reverse;
import io.parsingdata.metal.expression.value.UnaryValueExpression;
import io.parsingdata.metal.expression.value.Value;
Expand All @@ -62,6 +62,7 @@
import io.parsingdata.metal.expression.value.bitwise.ShiftLeft;
import io.parsingdata.metal.expression.value.bitwise.ShiftRight;
import io.parsingdata.metal.expression.value.reference.Count;
import io.parsingdata.metal.expression.value.reference.CurrentIteration;
import io.parsingdata.metal.expression.value.reference.CurrentOffset;
import io.parsingdata.metal.expression.value.reference.First;
import io.parsingdata.metal.expression.value.reference.Last;
Expand Down Expand Up @@ -89,6 +90,7 @@ public final class Shorthand {
public static final Token EMPTY = def(EMPTY_NAME, 0L);
public static final ValueExpression SELF = new Self();
public static final ValueExpression CURRENT_OFFSET = new CurrentOffset();
public static final ValueExpression CURRENT_ITERATION = new CurrentIteration();
public static final Expression TRUE = new True();

private Shorthand() {}
Expand Down
52 changes: 52 additions & 0 deletions core/src/main/java/io/parsingdata/metal/data/ImmutablePair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2013-2016 Netherlands Forensic Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.parsingdata.metal.data;

import static io.parsingdata.metal.Util.checkNotNull;

import java.util.Objects;

import io.parsingdata.metal.Util;

public class ImmutablePair<L, R> {

public final L left;
jvdb marked this conversation as resolved.
Show resolved Hide resolved
public final R right;

public ImmutablePair(final L left, final R right) {
this.left = checkNotNull(left, "left");
this.right = checkNotNull(right, "right");
}

@Override
public String toString() {
return left + "=" + right;
jvdb marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public boolean equals(final Object obj) {
return Util.notNullAndSameClass(this, obj)
&& Objects.equals(left, ((ImmutablePair)obj).left)
&& Objects.equals(right, ((ImmutablePair)obj).right);
}

@Override
public int hashCode() {
return Objects.hash(getClass(), left, right);
}

}
36 changes: 24 additions & 12 deletions core/src/main/java/io/parsingdata/metal/data/ParseState.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package io.parsingdata.metal.data;

import static java.math.BigInteger.ONE;
import static java.math.BigInteger.ZERO;

import static io.parsingdata.metal.Util.checkNotNegative;
Expand All @@ -36,43 +37,52 @@ public class ParseState {
public final ParseGraph order;
public final BigInteger offset;
public final Source source;
public final ImmutableList<ImmutablePair<Token, BigInteger>> iterations;

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

public static ParseState createFromByteStream(final ByteStream input, final BigInteger offset) {
return new ParseState(ParseGraph.EMPTY, new ByteStreamSource(input), offset);
return new ParseState(ParseGraph.EMPTY, new ByteStreamSource(input), offset, 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), source, offset);
return new ParseState(order.addBranch(token), source, offset, token.isIterable() ? iterations.add(new ImmutablePair<>(token, ZERO)) : iterations);
}

public ParseState closeBranch() {
return new ParseState(order.closeBranch(), source, offset);
public ParseState closeBranch(final Token token) {
if (token.isIterable() && !iterations.head.left.equals(token)) {
throw new IllegalStateException(String.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(), source, offset, token.isIterable() ? iterations.tail : iterations);
}

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

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

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

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

public ParseState source(final ValueExpression dataExpression, final int index, final ParseState parseState, final Encoding encoding) {
return new ParseState(order, new DataExpressionSource(dataExpression, index, parseState, encoding), ZERO);
return new ParseState(order, new DataExpressionSource(dataExpression, index, parseState, encoding), ZERO, iterations);
}

public Optional<Slice> slice(final BigInteger length) {
Expand All @@ -81,20 +91,22 @@ public Optional<Slice> slice(final BigInteger length) {

@Override
public String toString() {
return getClass().getSimpleName() + "(source:" + source + ";offset:" + offset + ";order:" + order + ")";
final String iterationString = iterations.isEmpty() ? "" : ";iterations:" + iterations.toString();
return getClass().getSimpleName() + "(source:" + source + ";offset:" + offset + ";order:" + order + iterationString + ")";
}

@Override
public boolean equals(final Object obj) {
return Util.notNullAndSameClass(this, obj)
&& Objects.equals(order, ((ParseState)obj).order)
&& Objects.equals(offset, ((ParseState)obj).offset)
&& Objects.equals(source, ((ParseState)obj).source);
&& Objects.equals(source, ((ParseState)obj).source)
&& Objects.equals(iterations, ((ParseState)obj).iterations);
}

@Override
public int hashCode() {
return Objects.hash(getClass(), order, offset, source);
return Objects.hash(getClass(), order, offset, source, iterations);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2013-2018 Netherlands Forensic Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

jvdb marked this conversation as resolved.
Show resolved Hide resolved
package io.parsingdata.metal.expression.value.reference;

import static io.parsingdata.metal.expression.value.ConstantFactory.createFromNumeric;

import java.util.Optional;

import io.parsingdata.metal.Util;
import io.parsingdata.metal.data.ImmutableList;
import io.parsingdata.metal.data.ParseState;
import io.parsingdata.metal.encoding.Encoding;
import io.parsingdata.metal.expression.value.Value;
import io.parsingdata.metal.expression.value.ValueExpression;
import io.parsingdata.metal.token.Rep;
import io.parsingdata.metal.token.RepN;
import io.parsingdata.metal.token.Token;
import io.parsingdata.metal.token.While;

/**
* A {@link ValueExpression} that represents the 0-based current iteration in an
* iterable {@link Token} (when {@link Token#isIterable()} returns true, e.g. when
* inside a {@link Rep}, {@link RepN}) or {@link While}).
*/
public class CurrentIteration implements ValueExpression {

@Override
public ImmutableList<Optional<Value>> eval(final ParseState parseState, final Encoding encoding) {
if (parseState.iterations.isEmpty()) {
return ImmutableList.create(Optional.empty());
}
return ImmutableList.create(Optional.of(createFromNumeric(parseState.iterations.head.right, new Encoding())));
}

@Override
public String toString() {
return getClass().getSimpleName();
}

@Override
public boolean equals(final Object obj) {
return Util.notNullAndSameClass(this, obj);
}

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

}
2 changes: 1 addition & 1 deletion core/src/main/java/io/parsingdata/metal/token/Cho.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private Trampoline<Optional<ParseState>> iterate(final Environment environment,
}
return list.head
.parse(environment)
.map(result -> complete(() -> success(result.closeBranch())))
.map(result -> complete(() -> success(result.closeBranch(this))))
.orElseGet(() -> intermediate(() -> iterate(environment, list.tail)));
}

Expand Down
81 changes: 81 additions & 0 deletions core/src/main/java/io/parsingdata/metal/token/IterableToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2013-2018 Netherlands Forensic Institute
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.parsingdata.metal.token;
rdvdijk marked this conversation as resolved.
Show resolved Hide resolved

import static io.parsingdata.metal.Trampoline.complete;
import static io.parsingdata.metal.Trampoline.intermediate;
import static io.parsingdata.metal.Util.checkNotNull;
import static io.parsingdata.metal.Util.success;

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

import io.parsingdata.metal.Trampoline;
import io.parsingdata.metal.data.Environment;
import io.parsingdata.metal.data.ParseState;
import io.parsingdata.metal.encoding.Encoding;

public abstract class IterableToken extends Token {

public final Token token;

IterableToken(final String name, final Token token, final Encoding encoding) {
super(name, encoding);
this.token = checkNotNull(token, "token");
}

protected final Optional<ParseState> parse(final Environment environment, final Predicate<Environment> stopCondition, final Function<Environment, Optional<ParseState>> ifIterationFails) {
return iterate(environment.addBranch(this), stopCondition, ifIterationFails).computeResult();
}

/**
* Iteratively parse iterations of the token, given a stop condition and the logic how to handle a failed parse.
jvdb marked this conversation as resolved.
Show resolved Hide resolved
*
* @param environment the environment to apply the parse to
* @param stopCondition a function to determine when to stop the iteration
* @param ifIterationFails a function to determine how to handle a failed parse
* @return a trampolined {@code Optional<ParseState>}
*/
private Trampoline<Optional<ParseState>> iterate(final Environment environment, final Predicate<Environment> stopCondition, final Function<Environment, Optional<ParseState>> ifIterationFails) {
if (stopCondition.test(environment)) {
return complete(() -> success(environment.parseState.closeBranch(this)));
}
return token
jvdb marked this conversation as resolved.
Show resolved Hide resolved
.parse(environment)
.map(nextParseState -> intermediate(() -> iterate(environment.withParseState(nextParseState.iterate()), stopCondition, ifIterationFails)))
.orElseGet(() -> complete(() -> ifIterationFails.apply(environment)));
}

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

@Override
public boolean equals(final Object obj) {
return super.equals(obj)
&& Objects.equals(token, ((IterableToken)obj).token);
}

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

}
2 changes: 1 addition & 1 deletion core/src/main/java/io/parsingdata/metal/token/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public Post(final String name, final Token token, final Expression predicate, fi
protected Optional<ParseState> parseImpl(final Environment environment) {
return token
.parse(environment.addBranch(this))
.map(nextParseState -> predicate.eval(nextParseState, environment.encoding) ? success(nextParseState.closeBranch()) : failure())
.map(nextParseState -> predicate.eval(nextParseState, environment.encoding) ? success(nextParseState.closeBranch(this)) : failure())
.orElseGet(Util::failure);
}

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/io/parsingdata/metal/token/Pre.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected Optional<ParseState> parseImpl(final Environment environment) {
}
return token
.parse(environment.addBranch(this))
.map(resultParseState -> success(resultParseState.closeBranch()))
.map(resultParseState -> success(resultParseState.closeBranch(this)))
.orElseGet(Util::failure);
}

Expand Down
Loading