-
Notifications
You must be signed in to change notification settings - Fork 9
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
Current iteration #242
Changes from 19 commits
e725c68
e6b2f37
0e12551
dac4afb
b449cff
a294916
ec291c2
c3c961e
cab9c3e
3450821
0d2401b
ad98634
f8f5034
a17967c
b2961aa
6fa43c5
ebfcf71
6b72849
fe71ac2
8f9d2c3
97a66c5
5442e7a
93716c3
fd803d8
c63f84c
840c354
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -36,43 +37,55 @@ public class ParseState { | |
public final ParseGraph order; | ||
public final BigInteger offset; | ||
public final Source source; | ||
public final ImmutableList<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<BigInteger> iterations) { | ||
this.order = checkNotNull(order, "order"); | ||
this.source = checkNotNull(source, "source"); | ||
this.offset = checkNotNegative(offset, "offset"); | ||
this.iterations = checkNotNull(iterations, "iteration"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor, missing s. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok |
||
} | ||
|
||
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); | ||
if (token.isIterable()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be a oneliner... Does @jvdb approve of this heresy? 🤣 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it could, but should it? ;) The code now clearly shows the difference between the two states, which will not be as clear if it is a oneliner. @jvdb will approve this, I'm sure! :D There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be entirely honest, seeing multiple identical There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, so you both would prefer:
? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer that yes 👍 |
||
return new ParseState(order.addBranch(token), source, offset, iterations.add(ZERO)); | ||
} | ||
return new ParseState(order.addBranch(token), source, offset, iterations); | ||
} | ||
|
||
public ParseState closeBranch() { | ||
return new ParseState(order.closeBranch(), source, offset); | ||
public ParseState closeBranch(final Token token) { | ||
if (token.isIterable()) { | ||
return new ParseState(order.closeBranch(), source, offset, iterations.tail); | ||
} | ||
return new ParseState(order.closeBranch(), source, offset, 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 iter() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. iterate/addIteration? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, iterate it is. |
||
return new ParseState(order, source, offset, iterations.tail.add(iterations.head.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) { | ||
|
@@ -81,20 +94,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,62 @@ | ||
/* | ||
* 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; | ||
|
||
/** | ||
* A {@link ValueExpression} that represents the 0-based current iteration in an | ||
* iterable {@link Token} (e.g. when inside a {@link Rep} or {@link RepN}). | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps also mention it can be checked if a Token is iterable, using a method reference link? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. |
||
public class CurrentIteration implements ValueExpression { | ||
|
||
@Override | ||
public ImmutableList<Optional<Value>> eval(final ParseState parseState, final Encoding encoding) { | ||
if (parseState.iterations.head == null) { | ||
jvdb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return ImmutableList.create(Optional.empty()); | ||
} | ||
return ImmutableList.create(Optional.of(createFromNumeric(parseState.iterations.head, new Encoding()))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default Side note: Don't we have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, there is not a default encoding as constant available. We do this 6 times in core; 4 in Shorthand, 1 in CurrentOffset, and 1 in CurrentIteration. In test scope we call it 7 times. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point: #245 |
||
} | ||
|
||
@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(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* 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 io.parsingdata.metal.Trampoline; | ||
import io.parsingdata.metal.data.Environment; | ||
import io.parsingdata.metal.data.ParseState; | ||
import io.parsingdata.metal.encoding.Encoding; | ||
|
||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
|
||
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; | ||
|
||
public abstract class IterableToken extends Token { | ||
rdvdijk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public final Token token; | ||
|
||
IterableToken(String name, final Token token, Encoding encoding) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing final. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. |
||
super(name, encoding); | ||
this.token = checkNotNull(token, "token"); | ||
} | ||
|
||
protected final Optional<ParseState> parse(final Environment environment, final Function<Environment, Boolean> stopCondition, final Function<Environment, Optional<ParseState>> ifIterationFails) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tnx! |
||
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 Function<Environment, Boolean> stopCondition, final Function<Environment, Optional<ParseState>> ifIterationFails) { | ||
if (stopCondition.apply(environment)) { | ||
return complete(() -> success(environment.parseState.closeBranch(this))); | ||
} | ||
return token | ||
jvdb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.parse(environment) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My head is a bit foggy, but isn't this an unsafe recursive call? If you would just nest iterable tokens which do not stop at the first iteration? |
||
.map(nextParseState -> intermediate(() -> iterate(environment.withParseState(nextParseState.iter()), 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); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something that now could happen is that
closeBranch(Token)
is called with aToken
that was not the one used foraddBranch(Token)
. This would of course be a bug, but since these methods arepublic
, we might need to guard for it. We could fix that by keeping track which token is used for the iteration. E.g. something like:Whenever we call
closeBranch(Token)
, we can verify that theToken
of the current iteration is indeed the one being passed. We could even passToken
toiter()
and be sure that we are iterating the expectedToken
(but that might be overkill).(Note: We have a
Pair
class inSelection
, but it is not generic.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion! I'm usually not a fan of using a Pair class, but I cannot think of an alternative, other then creating a new class, which would most likely be implemented as the Pair class. ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it in this case be an identity comparison instead of a (semantic) equality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that's a good point... If we want to really make sure we are using the same object, instead of just an equal one.