From 2f05dedfc9eee9b8a6724e77278d59ab3c047b22 Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Sun, 17 Feb 2019 14:40:23 +0100 Subject: [PATCH] #279, #70: Converted CurrentIteration's level operand to SingleValueExpression. Hint from @mvanaken. --- .../java/io/parsingdata/metal/Shorthand.java | 2 +- .../value/reference/CurrentIteration.java | 27 ++++++++------- .../CurrentIterationEdgeCaseTest.java | 33 +++++++++---------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index fc42c9ca..f501a833 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -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); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/CurrentIteration.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/CurrentIteration.java index 867f0318..7fb9e286 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/CurrentIteration.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/CurrentIteration.java @@ -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; @@ -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 level (a {@link SingleValueExpression} operand can be used + * to specify the relative nesting level of the iteration count that is required. + * 0 denotes the current (deepest) nesting level, 1 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 evalSingle(final ParseState parseState, final Encoding encoding) { - final BigInteger levelValue = getLevel(parseState, encoding); - if (parseState.iterations.size <= levelValue.longValue()) { - return Optional.empty(); + final Optional 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 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> getIterationRecursive(final ImmutableList> iterations, final BigInteger levelValue) { diff --git a/core/src/test/java/io/parsingdata/metal/expression/value/reference/CurrentIterationEdgeCaseTest.java b/core/src/test/java/io/parsingdata/metal/expression/value/reference/CurrentIterationEdgeCaseTest.java index 3c5c21d8..40313954 100644 --- a/core/src/test/java/io/parsingdata/metal/expression/value/reference/CurrentIterationEdgeCaseTest.java +++ b/core/src/test/java/io/parsingdata/metal/expression/value/reference/CurrentIterationEdgeCaseTest.java @@ -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; @@ -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; @@ -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 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 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 = iteration(con(-1)).eval(parseState, enc()); - assertTrue(value.isEmpty()); + Optional result = iteration(con(-1, signed())).evalSingle(parseState, enc()); + assertTrue(result.isPresent()); + assertEquals(NOT_A_VALUE, result.get()); } -} \ No newline at end of file +}