Skip to content

Commit

Permalink
#279, #70: Converted CurrentIteration's level operand to SingleValueE…
Browse files Browse the repository at this point in the history
…xpression. Hint from @mvanaken.
  • Loading branch information
jvdb committed Feb 17, 2019
1 parent b9e600d commit 2f05ded
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 32 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/io/parsingdata/metal/Shorthand.java
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ private Shorthand() {}
public static ValueExpression nth(final ValueExpression values, final ValueExpression indices) { return new Nth(values, indices); }
public static ValueExpression offset(final ValueExpression operand) { return new Offset(operand); }
public static SingleValueExpression iteration(final int level) { return iteration(con(level)); }
public static SingleValueExpression iteration(final ValueExpression level) { return new CurrentIteration(level); }
public static SingleValueExpression iteration(final SingleValueExpression level) { return new CurrentIteration(level); }
public static ValueExpression cat(final ValueExpression left, final ValueExpression right) { return new Cat(left, right); }
public static SingleValueExpression cat(final ValueExpression operand) { return new FoldCat(operand); }
public static ValueExpression elvis(final ValueExpression left, final ValueExpression right) { return new Elvis(left, right); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import io.parsingdata.metal.encoding.Encoding;
import io.parsingdata.metal.expression.value.SingleValueExpression;
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;
Expand All @@ -48,30 +47,30 @@
* A {@link SingleValueExpression} 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}).
*
* The <code>level</code> (a {@link SingleValueExpression} operand can be used
* to specify the relative nesting level of the iteration count that is required.
* <code>0</code> denotes the current (deepest) nesting level, <code>1</code> the
* level above it, and so on.
*/
public class CurrentIteration implements SingleValueExpression {

private final ValueExpression level;
private final SingleValueExpression level;

public CurrentIteration(final ValueExpression level) {
public CurrentIteration(final SingleValueExpression level) {
this.level = checkNotNull(level, "level");
}

@Override
public Optional<Value> evalSingle(final ParseState parseState, final Encoding encoding) {
final BigInteger levelValue = getLevel(parseState, encoding);
if (parseState.iterations.size <= levelValue.longValue()) {
return Optional.empty();
final Optional<Value> levelValue = level.evalSingle(parseState, encoding);
if (!levelValue.isPresent() || levelValue.get().equals(NOT_A_VALUE) || levelValue.get().asNumeric().compareTo(ZERO) < 0) {
return Optional.of(NOT_A_VALUE);
}
return getIterationRecursive(parseState.iterations, levelValue).computeResult();
}

private BigInteger getLevel(final ParseState parseState, final Encoding encoding) {
final ImmutableList<Value> levelValues = level.eval(parseState, encoding);
if (levelValues.size != 1 || levelValues.head.equals(NOT_A_VALUE)) {
throw new IllegalArgumentException("Level must evaluate to a single value.");
if (parseState.iterations.size <= levelValue.get().asNumeric().longValueExact()) {
return Optional.empty();
}
return levelValues.head.asNumeric();
return getIterationRecursive(parseState.iterations, levelValue.get().asNumeric()).computeResult();
}

private Trampoline<Optional<Value>> getIterationRecursive(final ImmutableList<ImmutablePair<Token, BigInteger>> iterations, final BigInteger levelValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.parsingdata.metal.expression.value.reference;

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

import static io.parsingdata.metal.Shorthand.con;
Expand All @@ -23,11 +24,15 @@
import static io.parsingdata.metal.Shorthand.last;
import static io.parsingdata.metal.Shorthand.ref;
import static io.parsingdata.metal.Shorthand.rep;
import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE;
import static io.parsingdata.metal.util.EncodingFactory.enc;
import static io.parsingdata.metal.util.EncodingFactory.signed;
import static io.parsingdata.metal.util.EnvironmentFactory.env;
import static io.parsingdata.metal.util.ParseStateFactory.stream;
import static io.parsingdata.metal.util.TokenDefinitions.any;

import java.util.Optional;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -49,30 +54,24 @@ public void before() {
parseState = rep(any("a")).parse(env(stream(1, 2, 3))).get();
}

@Test
public void multiLevel() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Level must evaluate to a single value.");
iteration(ref("a")).eval(parseState, enc());
}

@Test
public void emptyLevel() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Level must evaluate to a single value.");
iteration(last(ref("b"))).eval(parseState, enc());
Optional<Value> result = iteration(last(ref("b"))).evalSingle(parseState, enc());
assertTrue(result.isPresent());
assertEquals(NOT_A_VALUE, result.get());
}

@Test
public void emptyLevelHead() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Level must evaluate to a single value.");
iteration(div(con(1), con(0))).eval(parseState, enc());
public void NotAValueLevel() {
Optional<Value> result = iteration(last(div(con(1), con(0)))).evalSingle(parseState, enc());
assertTrue(result.isPresent());
assertEquals(NOT_A_VALUE, result.get());
}

@Test
public void negativeLevel() {
ImmutableList<Value> value = iteration(con(-1)).eval(parseState, enc());
assertTrue(value.isEmpty());
Optional<Value> result = iteration(con(-1, signed())).evalSingle(parseState, enc());
assertTrue(result.isPresent());
assertEquals(NOT_A_VALUE, result.get());
}
}
}

0 comments on commit 2f05ded

Please sign in to comment.