From a0699f6032d1ddf7fa7016159bc0c5ac5493f12b Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Mon, 11 Feb 2019 13:16:43 +0100 Subject: [PATCH 1/8] #70: Added SingleValueExpression interface. Converted size argument of Def to SingleValueExpression, along with all dependencies. --- .../java/io/parsingdata/metal/Shorthand.java | 35 +++++++------- .../metal/expression/value/Const.java | 10 ++-- .../metal/expression/value/CoreValue.java | 1 - .../value/SingleValueExpression.java | 46 +++++++++++++++++++ .../expression/value/reference/Last.java | 10 ++-- .../java/io/parsingdata/metal/token/Def.java | 27 ++++------- .../parsingdata/metal/AutoEqualityTest.java | 6 ++- .../io/parsingdata/metal/ByteLengthTest.java | 5 +- .../io/parsingdata/metal/DefSizeTest.java | 12 ++--- .../java/io/parsingdata/metal/ErrorsTest.java | 13 +++--- .../io/parsingdata/metal/ToStringTest.java | 2 +- .../io/parsingdata/metal/data/SliceTest.java | 4 +- .../value/reference/CurrentIterationTest.java | 3 +- .../metal/util/TokenDefinitions.java | 3 ++ .../io/parsingdata/metal/format/JPEG.java | 4 +- .../parsingdata/metal/format/VarIntTest.java | 2 +- 16 files changed, 116 insertions(+), 67 deletions(-) create mode 100644 core/src/main/java/io/parsingdata/metal/expression/value/SingleValueExpression.java diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index 0375a05e..1e9911f6 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -52,6 +52,7 @@ import io.parsingdata.metal.expression.value.FoldLeft; import io.parsingdata.metal.expression.value.FoldRight; import io.parsingdata.metal.expression.value.Reverse; +import io.parsingdata.metal.expression.value.SingleValueExpression; import io.parsingdata.metal.expression.value.UnaryValueExpression; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; @@ -97,15 +98,15 @@ public final class Shorthand { private Shorthand() {} - public static Token def(final String name, final ValueExpression size, final Expression predicate, final Encoding encoding) { return post(def(name, size, encoding), predicate); } - public static Token def(final String name, final ValueExpression size, final Expression predicate) { return def(name, size, predicate, null); } - public static Token def(final String name, final ValueExpression size, final Encoding encoding) { return new Def(name, size, encoding); } - public static Token def(final String name, final ValueExpression size) { return def(name, size, (Encoding)null); } + public static Token def(final String name, final SingleValueExpression size, final Expression predicate, final Encoding encoding) { return post(def(name, size, encoding), predicate); } + public static Token def(final String name, final SingleValueExpression size, final Expression predicate) { return def(name, size, predicate, null); } + public static Token def(final String name, final SingleValueExpression size, final Encoding encoding) { return new Def(name, size, encoding); } + public static Token def(final String name, final SingleValueExpression size) { return def(name, size, (Encoding)null); } public static Token def(final String name, final long size, final Expression predicate, final Encoding encoding) { return def(name, con(size), predicate, encoding); } public static Token def(final String name, final long size, final Expression predicate) { return def(name, size, predicate, null); } public static Token def(final String name, final long size, final Encoding encoding) { return def(name, con(size), encoding); } public static Token def(final String name, final long size) { return def(name, size, (Encoding)null); } - public static Token nod(final ValueExpression size) { return def(EMPTY_NAME, size); } + public static Token nod(final SingleValueExpression size) { return def(EMPTY_NAME, size); } public static Token nod(final long size) { return nod(con(size)); } public static Token cho(final String name, final Encoding encoding, final Token token1, final Token token2, final Token... tokens) { return new Cho(name, encoding, token1, token2, tokens); } public static Token cho(final String name, final Token token1, final Token token2, final Token... tokens) { return cho(name, null, token1, token2, tokens); } @@ -172,24 +173,24 @@ private Shorthand() {} public static UnaryValueExpression not(final ValueExpression operand) { return new io.parsingdata.metal.expression.value.bitwise.Not(operand); } public static BinaryValueExpression shl(final ValueExpression left, final ValueExpression right) { return new ShiftLeft(left, right); } public static BinaryValueExpression shr(final ValueExpression left, final ValueExpression right) { return new ShiftRight(left, right); } - public static ValueExpression con(final long value) { return con(value, DEFAULT_ENCODING); } - public static ValueExpression con(final long value, final Encoding encoding) { return con(ConstantFactory.createFromNumeric(value, encoding)); } - public static ValueExpression con(final String value) { return con(value, DEFAULT_ENCODING); } - public static ValueExpression con(final String value, final Encoding encoding) { return con(ConstantFactory.createFromString(value, encoding)); } - public static ValueExpression con(final Value value) { return new Const(value); } - public static ValueExpression con(final Encoding encoding, final int... values) { return new Const(new CoreValue(createFromBytes(toByteArray(values)), encoding)); } - public static ValueExpression con(final int... values) { return con(DEFAULT_ENCODING, values); } - public static ValueExpression con(final byte[] value) { return con(value, DEFAULT_ENCODING); } - public static ValueExpression con(final byte[] value, final Encoding encoding) { return con(ConstantFactory.createFromBytes(value, encoding)); } + public static SingleValueExpression con(final long value) { return con(value, DEFAULT_ENCODING); } + public static SingleValueExpression con(final long value, final Encoding encoding) { return con(ConstantFactory.createFromNumeric(value, encoding)); } + public static SingleValueExpression con(final String value) { return con(value, DEFAULT_ENCODING); } + public static SingleValueExpression con(final String value, final Encoding encoding) { return con(ConstantFactory.createFromString(value, encoding)); } + public static SingleValueExpression con(final Value value) { return new Const(value); } + public static SingleValueExpression con(final Encoding encoding, final int... values) { return new Const(new CoreValue(createFromBytes(toByteArray(values)), encoding)); } + public static SingleValueExpression con(final int... values) { return con(DEFAULT_ENCODING, values); } + public static SingleValueExpression con(final byte[] value) { return con(value, DEFAULT_ENCODING); } + public static SingleValueExpression con(final byte[] value, final Encoding encoding) { return con(ConstantFactory.createFromBytes(value, encoding)); } public static ValueExpression len(final ValueExpression operand) { return new Len(operand); } public static NameRef ref(final String name) { return ref(name, null); } public static NameRef ref(final String name, final ValueExpression limit) { return new NameRef(name, limit); } public static DefinitionRef ref(final Token definition) { return ref(definition, null); } public static DefinitionRef ref(final Token definition, final ValueExpression limit) { return new DefinitionRef(definition, limit); } public static ValueExpression first(final ValueExpression operand) { return new First(operand); } - public static ValueExpression last(final ValueExpression operand) { return new Last(operand); } - public static ValueExpression last(final NameRef operand) { return new Last(new NameRef(operand.reference, con(1))); } - public static ValueExpression last(final DefinitionRef operand) { return new Last(new DefinitionRef(operand.reference, con(1))); } + public static SingleValueExpression last(final ValueExpression operand) { return new Last(operand); } + public static SingleValueExpression last(final NameRef operand) { return new Last(new NameRef(operand.reference, con(1))); } + public static SingleValueExpression last(final DefinitionRef operand) { return new Last(new DefinitionRef(operand.reference, con(1))); } 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 ValueExpression iteration(final int level) { return iteration(con(level)); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Const.java b/core/src/main/java/io/parsingdata/metal/expression/value/Const.java index b8c960bc..f1b3d533 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Const.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Const.java @@ -17,19 +17,19 @@ package io.parsingdata.metal.expression.value; import java.util.Objects; +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; /** - * A {@link ValueExpression} representing a constant value. + * A {@link SingleValueExpression} representing a constant value. *

* Const has a single operand value (a {@link Value}). When * evaluated, this value is returned. */ -public class Const implements ValueExpression { +public class Const implements SingleValueExpression { public final Value value; @@ -38,8 +38,8 @@ public Const(final Value value) { } @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { - return ImmutableList.create(value); + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { + return Optional.of(value); } @Override diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/CoreValue.java b/core/src/main/java/io/parsingdata/metal/expression/value/CoreValue.java index cc5212ec..49e1517e 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/CoreValue.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/CoreValue.java @@ -18,7 +18,6 @@ import static io.parsingdata.metal.Util.bytesToHexString; import static io.parsingdata.metal.Util.checkNotNull; -import static io.parsingdata.metal.encoding.Encoding.DEFAULT_ENCODING; import java.math.BigInteger; import java.util.BitSet; diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/SingleValueExpression.java b/core/src/main/java/io/parsingdata/metal/expression/value/SingleValueExpression.java new file mode 100644 index 00000000..0bdfc5bd --- /dev/null +++ b/core/src/main/java/io/parsingdata/metal/expression/value/SingleValueExpression.java @@ -0,0 +1,46 @@ +/* + * 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.expression.value; + +import java.util.Optional; + +import io.parsingdata.metal.data.ImmutableList; +import io.parsingdata.metal.data.ParseState; +import io.parsingdata.metal.encoding.Encoding; + +/** + * Interface for all SingleValueExpression implementations. + *

+ * A SingleValueExpression is an expression that is evaluated by executing its + * {@link #eval(ParseState, Encoding)} method. It yields an {@link Optional} + * {@link Value} object. + *

+ * As context, it receives the current ParseState object as + * well as the current Encoding object. + */ +@SuppressWarnings("FunctionalInterfaceMethodChanged") // What we do is in line with error-prone's advice +@FunctionalInterface +public interface SingleValueExpression extends ValueExpression { + + Optional evalSingle(ParseState parseState, Encoding encoding); + + @Override + default ImmutableList eval(ParseState parseState, Encoding encoding) { + return evalSingle(parseState, encoding).map(ImmutableList::create).orElseGet(ImmutableList::new); + } + +} diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Last.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Last.java index b46f4627..b6c55728 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Last.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Last.java @@ -19,19 +19,21 @@ import static io.parsingdata.metal.Util.checkNotNull; import java.util.Objects; +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.SingleValueExpression; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; /** - * A {@link ValueExpression} that represents the last {@link Value} returned + * A {@link SingleValueExpression} that represents the last {@link Value} returned * by evaluating its operand. */ -public class Last implements ValueExpression { +public class Last implements SingleValueExpression { public final ValueExpression operand; @@ -40,9 +42,9 @@ public Last(final ValueExpression operand) { } @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { final ImmutableList values = operand.eval(parseState, encoding); - return values.isEmpty() ? values : ImmutableList.create(values.head); + return values.isEmpty() ? Optional.empty() : Optional.of(values.head); } @Override diff --git a/core/src/main/java/io/parsingdata/metal/token/Def.java b/core/src/main/java/io/parsingdata/metal/token/Def.java index 8ef4c7f4..2fddf46e 100644 --- a/core/src/main/java/io/parsingdata/metal/token/Def.java +++ b/core/src/main/java/io/parsingdata/metal/token/Def.java @@ -20,7 +20,6 @@ import static io.parsingdata.metal.Util.checkNotEmpty; import static io.parsingdata.metal.Util.checkNotNull; -import static io.parsingdata.metal.Util.failure; import static io.parsingdata.metal.Util.success; import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; @@ -28,49 +27,43 @@ import java.util.Objects; import java.util.Optional; -import io.parsingdata.metal.Util; import io.parsingdata.metal.data.Environment; -import io.parsingdata.metal.data.ImmutableList; import io.parsingdata.metal.data.ParseState; import io.parsingdata.metal.data.ParseValue; import io.parsingdata.metal.encoding.Encoding; -import io.parsingdata.metal.expression.value.Value; -import io.parsingdata.metal.expression.value.ValueExpression; +import io.parsingdata.metal.expression.value.SingleValueExpression; /** * A {@link Token} that specifies a value to parse in the input. *

- * A Def consists of a size (a {@link ValueExpression}. + * A Def consists of a size (a {@link SingleValueExpression}. *

- * Parsing will succeed if size evaluates to a single value and if + * Parsing will succeed if size evaluates to a value and if * that many bytes are available in the input. This means that a size of zero * will lead to a successful parse, but will not produce a value. * - * @see ValueExpression + * @see SingleValueExpression */ public class Def extends Token { - public final ValueExpression size; + public final SingleValueExpression size; - public Def(final String name, final ValueExpression size, final Encoding encoding) { + public Def(final String name, final SingleValueExpression size, final Encoding encoding) { super(checkNotEmpty(name, "name"), encoding); this.size = checkNotNull(size, "size"); } @Override protected Optional parseImpl(final Environment environment) { - final ImmutableList sizes = size.eval(environment.parseState, environment.encoding); - if (sizes.size != 1 || sizes.head.equals(NOT_A_VALUE)) { - return failure(); - } - return sizes.head.asNumeric().compareTo(ZERO) != 0 ? slice(environment, sizes.head.asNumeric()) : success(environment.parseState); + return size.evalSingle(environment.parseState, environment.encoding) + .filter(size -> !size.equals(NOT_A_VALUE)) + .flatMap(size -> size.asNumeric().compareTo(ZERO) != 0 ? slice(environment, size.asNumeric()) : success(environment.parseState)); } private Optional slice(final Environment environment, final BigInteger dataSize) { return environment.parseState .slice(dataSize) - .map(slice -> environment.parseState.add(new ParseValue(environment.scope, this, slice, environment.encoding)).seek(dataSize.add(environment.parseState.offset))) - .orElseGet(Util::failure); + .flatMap(slice -> environment.parseState.add(new ParseValue(environment.scope, this, slice, environment.encoding)).seek(dataSize.add(environment.parseState.offset))); } @Override diff --git a/core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java b/core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java index 0733b4db..dc094fe0 100644 --- a/core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java +++ b/core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java @@ -26,6 +26,7 @@ import static io.parsingdata.metal.Shorthand.TRUE; import static io.parsingdata.metal.Shorthand.con; +import static io.parsingdata.metal.Shorthand.exp; import static io.parsingdata.metal.Shorthand.not; import static io.parsingdata.metal.Shorthand.ref; import static io.parsingdata.metal.data.ByteStreamSourceTest.DUMMY_BYTE_STREAM_SOURCE; @@ -87,6 +88,7 @@ import io.parsingdata.metal.expression.value.FoldLeft; import io.parsingdata.metal.expression.value.FoldRight; import io.parsingdata.metal.expression.value.Reverse; +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.expression.value.arithmetic.Add; @@ -147,7 +149,8 @@ public class AutoEqualityTest { private static final List> ENCODINGS = Arrays.asList(EncodingFactory::enc, EncodingFactory::signed, EncodingFactory::le, () -> new Encoding(Charset.forName("UTF-8"))); private static final List> TOKENS = Arrays.asList(() -> any("a"), () -> any("b")); private static final List> TOKEN_ARRAYS = Arrays.asList(() -> new Token[] { any("a"), any("b")}, () -> new Token[] { any("b"), any("c") }, () -> new Token[] { any("a"), any("b"), any("c") }); - private static final List> VALUE_EXPRESSIONS = Arrays.asList(() -> con(1), () -> con(2)); + private static final List> SINGLE_VALUE_EXPRESSIONS = Arrays.asList(() -> con(1), () -> con(2)); + private static final List> VALUE_EXPRESSIONS = Arrays.asList(() -> con(1), () -> exp(con(1), con(2))); private static final List> EXPRESSIONS = Arrays.asList(() -> TRUE, () -> not(TRUE)); private static final List> VALUES = Arrays.asList(() -> ConstantFactory.createFromString("a", enc()), () -> ConstantFactory.createFromString("b", enc()), () -> ConstantFactory.createFromNumeric(1L, signed()), () -> NOT_A_VALUE); private static final List> REDUCERS = Arrays.asList(() -> (BinaryOperator) Shorthand::cat, () -> (BinaryOperator) Shorthand::div); @@ -170,6 +173,7 @@ private static Map>> buildMap() { result.put(Encoding.class, ENCODINGS); result.put(Token.class, TOKENS); result.put(Token[].class, TOKEN_ARRAYS); + result.put(SingleValueExpression.class, SINGLE_VALUE_EXPRESSIONS); result.put(ValueExpression.class, VALUE_EXPRESSIONS); result.put(Expression.class, EXPRESSIONS); result.put(Value.class, VALUES); diff --git a/core/src/test/java/io/parsingdata/metal/ByteLengthTest.java b/core/src/test/java/io/parsingdata/metal/ByteLengthTest.java index e97412dd..7d19d4c9 100644 --- a/core/src/test/java/io/parsingdata/metal/ByteLengthTest.java +++ b/core/src/test/java/io/parsingdata/metal/ByteLengthTest.java @@ -24,6 +24,7 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.def; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.len; import static io.parsingdata.metal.Shorthand.ltNum; import static io.parsingdata.metal.Shorthand.ref; @@ -53,8 +54,8 @@ public class ByteLengthTest { // but Len will become useful when Let is implemented private static final Token STRING = seq( def("length", 1), - def("text1", ref("length")), - def("text2", len(ref("text1")))); + def("text1", last(ref("length"))), + def("text2", last(len(ref("text1"))))); // let("hasText", con(true), ltNum(len(ref("text1")), con(0)))); private static final Token NAME = diff --git a/core/src/test/java/io/parsingdata/metal/DefSizeTest.java b/core/src/test/java/io/parsingdata/metal/DefSizeTest.java index c57f8cd0..2628317d 100644 --- a/core/src/test/java/io/parsingdata/metal/DefSizeTest.java +++ b/core/src/test/java/io/parsingdata/metal/DefSizeTest.java @@ -24,6 +24,7 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.def; import static io.parsingdata.metal.Shorthand.eq; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.ref; import static io.parsingdata.metal.Shorthand.seq; import static io.parsingdata.metal.data.ParseState.createFromByteStream; @@ -32,8 +33,7 @@ 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.EMPTY_VE; -import static io.parsingdata.metal.util.TokenDefinitions.any; +import static io.parsingdata.metal.util.TokenDefinitions.EMPTY_SVE; import java.util.Optional; @@ -48,7 +48,7 @@ public class DefSizeTest { public static final Token FORMAT = seq( def("length", con(4)), - def("data", ref("length")) + def("data", last(ref("length"))) ); @Test @@ -78,10 +78,8 @@ public void testInvalidLength() { } @Test - public void testEmptyLengthInList() { - assertFalse(def("a", EMPTY_VE).parse(env(stream(1, 2, 3, 4))).isPresent()); - final Token aList = seq(any("a"), any("a")); - assertFalse(seq(aList, def("b", ref("a"))).parse(env(stream(1, 2, 3, 4))).isPresent()); + public void testLengthNotAValue() { + assertFalse(def("a", EMPTY_SVE).parse(env(stream(1, 2, 3, 4))).isPresent()); } @Test diff --git a/core/src/test/java/io/parsingdata/metal/ErrorsTest.java b/core/src/test/java/io/parsingdata/metal/ErrorsTest.java index 00dbe13f..954092a5 100644 --- a/core/src/test/java/io/parsingdata/metal/ErrorsTest.java +++ b/core/src/test/java/io/parsingdata/metal/ErrorsTest.java @@ -23,6 +23,7 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.def; import static io.parsingdata.metal.Shorthand.div; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.mul; import static io.parsingdata.metal.Shorthand.neg; import static io.parsingdata.metal.Shorthand.ref; @@ -53,22 +54,22 @@ public class ErrorsTest { public void noValueForSize() { thrown = ExpectedException.none(); // Basic division by zero. - final Token nanSize = def("a", div(con(1), con(0))); + final Token nanSize = def("a", last(div(con(1), con(0)))); assertFalse(nanSize.parse(env(stream(1))).isPresent()); // Try to negate division by zero. - final Token negNanSize = def("a", neg(div(con(1), con(0)))); + final Token negNanSize = def("a", last(neg(div(con(1), con(0))))); assertFalse(negNanSize.parse(env(stream(1))).isPresent()); // Add one to division by zero. - final Token addNanSize = def("a", add(div(con(1), con(0)), con(1))); + final Token addNanSize = def("a", last(add(div(con(1), con(0)), con(1)))); assertFalse(addNanSize.parse(env(stream(1))).isPresent()); // Add division by zero to one. - final Token addNanSize2 = def("a", add(con(1), div(con(1), con(0)))); + final Token addNanSize2 = def("a", last(add(con(1), div(con(1), con(0))))); assertFalse(addNanSize2.parse(env(stream(1))).isPresent()); // Subtract one from division by zero. - final Token subNanSize = def("a", sub(div(con(1), con(0)), con(1))); + final Token subNanSize = def("a", last(sub(div(con(1), con(0)), con(1)))); assertFalse(subNanSize.parse(env(stream(1))).isPresent()); // Multiply division by zero with one. - final Token mulNanSize = def("a", mul(div(con(1), con(0)), con(1))); + final Token mulNanSize = def("a", last(mul(div(con(1), con(0)), con(1)))); assertFalse(mulNanSize.parse(env(stream(1))).isPresent()); } diff --git a/core/src/test/java/io/parsingdata/metal/ToStringTest.java b/core/src/test/java/io/parsingdata/metal/ToStringTest.java index 209a7a9b..d0cf3379 100644 --- a/core/src/test/java/io/parsingdata/metal/ToStringTest.java +++ b/core/src/test/java/io/parsingdata/metal/ToStringTest.java @@ -107,7 +107,7 @@ public void before() { @Test public void validateToStringImplementation() { final Expression e = not(and(eq(v(), v()), or(eqNum(v()), and(eqStr(v()), or(gtEqNum(v()), or(gtNum(v()), or(ltEqNum(v()), ltNum(v())))))))); - final Token t = until("untilName", v(), v(), v(), post(repn(sub(opt(pre(rep(cho(token("refName"), any(n()), seq(nod(10), tie(def(n(), v()), v()), whl(def(n(), con(1), e), e), tie(t(), con(1))))), e)), v()), v()), e)); + final Token t = until("untilName", v(), v(), v(), post(repn(sub(opt(pre(rep(cho(token("refName"), any(n()), seq(nod(10), tie(def(n(), last(v())), v()), whl(def(n(), con(1), e), e), tie(t(), con(1))))), e)), v()), v()), e)); final String output = t.toString(); for (int i = 0; i < count; i++) { assertTrue(output.contains(prefix + i)); diff --git a/core/src/test/java/io/parsingdata/metal/data/SliceTest.java b/core/src/test/java/io/parsingdata/metal/data/SliceTest.java index 0e5e1add..9e2019cd 100644 --- a/core/src/test/java/io/parsingdata/metal/data/SliceTest.java +++ b/core/src/test/java/io/parsingdata/metal/data/SliceTest.java @@ -73,9 +73,9 @@ public void lazyLength() { final ReadTrackingByteStream stream = new ReadTrackingByteStream(new InMemoryByteStream(toByteArray(1, 2, 3, 0, 0, 0, 4, 1))); final Optional result = seq(def("a", con(3)), - post(def("b", len(last(ref("a")))), eq(con(0, 0, 0))), + post(def("b", last(len(last(ref("a"))))), eq(con(0, 0, 0))), def("c", con(1)), - post(def("d", len(last(ref("c")))), eq(con(1)))).parse(env(createFromByteStream(stream), enc())); + post(def("d", last(len(last(ref("c"))))), eq(con(1)))).parse(env(createFromByteStream(stream), enc())); assertTrue(result.isPresent()); assertTrue(stream.containsAll(3, 4, 5, 7)); assertTrue(stream.containsNone(0, 1, 2, 6)); diff --git a/core/src/test/java/io/parsingdata/metal/expression/value/reference/CurrentIterationTest.java b/core/src/test/java/io/parsingdata/metal/expression/value/reference/CurrentIterationTest.java index 01d3d909..2289e144 100644 --- a/core/src/test/java/io/parsingdata/metal/expression/value/reference/CurrentIterationTest.java +++ b/core/src/test/java/io/parsingdata/metal/expression/value/reference/CurrentIterationTest.java @@ -23,6 +23,7 @@ import static io.parsingdata.metal.Shorthand.eq; import static io.parsingdata.metal.Shorthand.eqNum; import static io.parsingdata.metal.Shorthand.iteration; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.not; import static io.parsingdata.metal.Shorthand.nth; import static io.parsingdata.metal.Shorthand.ref; @@ -62,7 +63,7 @@ public static Collection data() { { "[0] CURRENT_ITERATION", VALUE_EQ_ITERATION, stream(0), enc(), false }, { "[0, 1] seq(!CURRENT_ITERATION, ...)", seq(VALUE_NOT_EQ_ITERATION, VALUE_NOT_EQ_ITERATION), stream(0, 1), enc(), true }, { "[0] !CURRENT_ITERATION", VALUE_NOT_EQ_ITERATION, stream(0), enc(), true }, - { "[0 | 0, 1 | 0, 0, 2 | 0, 0, 0, 3] rep(CURRENT_ITERATION)", rep(def("value", add(CURRENT_ITERATION, con(1)), eqNum(CURRENT_ITERATION))), stream(0, 0, 1, 0, 0, 2, 0, 0, 0, 3), enc(), true }, + { "[0 | 0, 1 | 0, 0, 2 | 0, 0, 0, 3] rep(CURRENT_ITERATION)", rep(def("value", last(add(CURRENT_ITERATION, con(1))), eqNum(CURRENT_ITERATION))), stream(0, 0, 1, 0, 0, 2, 0, 0, 0, 3), enc(), true }, { "[1, 1, 0, 1 | 0 | 1 | 3] repn=4(def(size)), repn=4(if(sizeRef(CURRENT_ITERATION) != 0, def(CURRENT_ITERATION)))", seq(repn(def("size", 1), con(4)), repn(when(def("value", con(1), eq(CURRENT_ITERATION)), not(eq(nth(ref("size"), CURRENT_ITERATION), con(0)))), con(4))), stream(1, 1, 0, 1, 0, 1, 3), enc(), true }, }); } diff --git a/core/src/test/java/io/parsingdata/metal/util/TokenDefinitions.java b/core/src/test/java/io/parsingdata/metal/util/TokenDefinitions.java index 3b52f750..185010ea 100644 --- a/core/src/test/java/io/parsingdata/metal/util/TokenDefinitions.java +++ b/core/src/test/java/io/parsingdata/metal/util/TokenDefinitions.java @@ -19,16 +19,19 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.def; import static io.parsingdata.metal.Shorthand.div; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.not; import static io.parsingdata.metal.Shorthand.ref; import io.parsingdata.metal.Shorthand; +import io.parsingdata.metal.expression.value.SingleValueExpression; import io.parsingdata.metal.expression.value.ValueExpression; import io.parsingdata.metal.token.Token; public class TokenDefinitions { public static final ValueExpression EMPTY_VE = div(con(1), con(0)); // division by zero to wrap empty value + public static final SingleValueExpression EMPTY_SVE = last(EMPTY_VE); // same for SingleValueExpression private TokenDefinitions() {} diff --git a/formats/src/main/java/io/parsingdata/metal/format/JPEG.java b/formats/src/main/java/io/parsingdata/metal/format/JPEG.java index ac44e829..59c621dc 100644 --- a/formats/src/main/java/io/parsingdata/metal/format/JPEG.java +++ b/formats/src/main/java/io/parsingdata/metal/format/JPEG.java @@ -61,14 +61,14 @@ private JPEG() {} def(MARKER, con(1), eq(con(0xff))), def(IDENTIFIER, con(1), or(ltNum(con(0xd8)), gtNum(con(0xda)))), def(LENGTH, con(2)), - def(PAYLOAD, sub(last(ref(LENGTH)), con(2)))); + def(PAYLOAD, last(sub(last(ref(LENGTH)), con(2))))); private static final Token SCAN_SEGMENT = seq("scan segment", def(MARKER, con(1), eq(con(0xff))), def(IDENTIFIER, con(1), eq(con(0xda))), def(LENGTH, con(2)), - def(PAYLOAD, sub(last(ref(LENGTH)), con(2))), + def(PAYLOAD, last(sub(last(ref(LENGTH)), con(2)))), rep(cho(def("scandata", con(1), not(eq(con(0xff)))), def("escape", con(2), or(eq(con(0xff00)), and(gtNum(con(0xffcf)), ltNum(con(0xffd8)))))))); diff --git a/formats/src/test/java/io/parsingdata/metal/format/VarIntTest.java b/formats/src/test/java/io/parsingdata/metal/format/VarIntTest.java index ce150f4f..64aff3ad 100644 --- a/formats/src/test/java/io/parsingdata/metal/format/VarIntTest.java +++ b/formats/src/test/java/io/parsingdata/metal/format/VarIntTest.java @@ -50,7 +50,7 @@ public static Token varIntAndValue(final int size) { repn( seq( varInt("varInt"), - post(def("decoded", len(decodeVarInt(last(ref("varInt"))))), eq(decodeVarInt(last(ref("varInt"))))) + post(def("decoded", last(len(decodeVarInt(last(ref("varInt")))))), eq(decodeVarInt(last(ref("varInt"))))) ), con(4)); @Parameterized.Parameters(name = "{0} ({4})") From 51c19862ab200db48f9e5cf3f160ac0937ff0f53 Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Mon, 11 Feb 2019 22:12:50 +0100 Subject: [PATCH 2/8] #70: Converted n argument of RepN to SingleValueExpression, along with all dependencies. --- .../java/io/parsingdata/metal/Shorthand.java | 8 +++--- .../java/io/parsingdata/metal/token/RepN.java | 26 +++++++------------ .../java/io/parsingdata/metal/ErrorsTest.java | 12 --------- .../io/parsingdata/metal/IterateTest.java | 5 ++-- .../parsingdata/metal/SubStructTableTest.java | 3 ++- .../io/parsingdata/metal/ToStringTest.java | 2 +- .../expression/value/NthExpressionTest.java | 5 ++-- 7 files changed, 23 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index 1e9911f6..313df7ea 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -116,10 +116,10 @@ private Shorthand() {} public static Token rep(final String name, final Token token) { return rep(name, token, null); } public static Token rep(final Token token, final Encoding encoding) { return rep(NO_NAME, token, encoding); } public static Token rep(final Token token) { return rep(token, null); } - public static Token repn(final String name, final Token token, final ValueExpression n, final Encoding encoding) { return new RepN(name, token, n, encoding); } - public static Token repn(final String name, final Token token, final ValueExpression n) { return repn(name, token, n, null); } - public static Token repn(final Token token, final ValueExpression n, final Encoding encoding) { return repn(NO_NAME, token, n, encoding); } - public static Token repn(final Token token, final ValueExpression n) { return repn(token, n, null); } + public static Token repn(final String name, final Token token, final SingleValueExpression n, final Encoding encoding) { return new RepN(name, token, n, encoding); } + public static Token repn(final String name, final Token token, final SingleValueExpression n) { return repn(name, token, n, null); } + public static Token repn(final Token token, final SingleValueExpression n, final Encoding encoding) { return repn(NO_NAME, token, n, encoding); } + public static Token repn(final Token token, final SingleValueExpression n) { return repn(token, n, null); } public static Token seq(final String name, final Encoding encoding, final Token token1, final Token token2, final Token... tokens) { return new Seq(name, encoding, token1, token2, tokens); } public static Token seq(final String name, final Token token1, final Token token2, final Token... tokens) { return seq(name, null, token1, token2, tokens); } public static Token seq(final Encoding encoding, final Token token1, final Token token2, final Token... tokens) { return seq(NO_NAME, encoding, token1, token2, tokens); } diff --git a/core/src/main/java/io/parsingdata/metal/token/RepN.java b/core/src/main/java/io/parsingdata/metal/token/RepN.java index 4eb982b2..65ec83b9 100644 --- a/core/src/main/java/io/parsingdata/metal/token/RepN.java +++ b/core/src/main/java/io/parsingdata/metal/token/RepN.java @@ -20,48 +20,42 @@ import static io.parsingdata.metal.Util.failure; import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; -import java.math.BigInteger; import java.util.Objects; import java.util.Optional; import io.parsingdata.metal.data.Environment; -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.SingleValueExpression; import io.parsingdata.metal.expression.value.ValueExpression; /** * A {@link Token} that specifies a bounded repetition of a token. *

* A RepN consists of a token (a {@link Token}) and an - * n (a {@link ValueExpression}). First n is - * evaluated. Parsing fails if it does not evaluate to a single value. The - * token is then parsed for an amount of times equal to the evaluated value of - * n. RepN succeeds if this succeeds. + * n (a {@link SingleValueExpression}). First n is + * evaluated. The token is then parsed for an amount of times equal to the + * evaluated value of n. RepN succeeds if this succeeds. * * @see Rep * @see ValueExpression */ public class RepN extends IterableToken { - public final ValueExpression n; + public final SingleValueExpression n; - public RepN(final String name, final Token token, final ValueExpression n, final Encoding encoding) { + public RepN(final String name, final Token token, final SingleValueExpression n, final Encoding encoding) { super(name, token, encoding); this.n = checkNotNull(n, "n"); } @Override protected Optional parseImpl(final Environment environment) { - final ImmutableList counts = n.eval(environment.parseState, environment.encoding); - if (counts.size != 1 || counts.head.equals(NOT_A_VALUE)) { - return failure(); - } - final BigInteger count = counts.head.asNumeric(); - return parse(environment, env -> env.parseState.iterations.head.right.compareTo(count) >= 0, env -> failure()); + return n.evalSingle(environment.parseState, environment.encoding) + .filter(count -> !count.equals(NOT_A_VALUE)) + .flatMap(count -> parse(environment, env -> env.parseState.iterations.head.right.compareTo(count.asNumeric()) >= 0, env -> failure())); } - + @Override public String toString() { return getClass().getSimpleName() + "(" + makeNameFragment() + token + "," + n + ")"; diff --git a/core/src/test/java/io/parsingdata/metal/ErrorsTest.java b/core/src/test/java/io/parsingdata/metal/ErrorsTest.java index 954092a5..f9866cf1 100644 --- a/core/src/test/java/io/parsingdata/metal/ErrorsTest.java +++ b/core/src/test/java/io/parsingdata/metal/ErrorsTest.java @@ -73,18 +73,6 @@ public void noValueForSize() { assertFalse(mulNanSize.parse(env(stream(1))).isPresent()); } - @Test - public void multiValueInRepN() { - final Token dummy = any("a"); - final Token multiRepN = - seq(any("b"), - any("b"), - repn(dummy, ref("b")) - ); - Optional result = multiRepN.parse(env(stream(2, 2, 2, 2))); - assertFalse(result.isPresent()); - } - @Test public void parseStateWithNegativeOffset() { thrown.expect(IllegalArgumentException.class); diff --git a/core/src/test/java/io/parsingdata/metal/IterateTest.java b/core/src/test/java/io/parsingdata/metal/IterateTest.java index 91e192dc..97182700 100644 --- a/core/src/test/java/io/parsingdata/metal/IterateTest.java +++ b/core/src/test/java/io/parsingdata/metal/IterateTest.java @@ -21,6 +21,7 @@ import static io.parsingdata.metal.Shorthand.div; import static io.parsingdata.metal.Shorthand.eq; import static io.parsingdata.metal.Shorthand.gtNum; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.ref; import static io.parsingdata.metal.Shorthand.repn; import static io.parsingdata.metal.Shorthand.seq; @@ -41,11 +42,11 @@ public class IterateTest extends ParameterizedParse { private static final Token repNToken = seq(any("n"), - repn(def("x", con(1), gtNum(con(1))), ref("n")), + repn(def("x", con(1), gtNum(con(1))), last(ref("n"))), def("f", con(1), eq(con(42)))); private static final Token repBrokenNToken = - seq(repn(any("x"), div(con(1), con(0))), + seq(repn(any("x"), last(div(con(1), con(0)))), def("f", con(1), eq(con(42)))); @Parameters(name = "{0} ({4})") diff --git a/core/src/test/java/io/parsingdata/metal/SubStructTableTest.java b/core/src/test/java/io/parsingdata/metal/SubStructTableTest.java index 72f80b18..66f60931 100644 --- a/core/src/test/java/io/parsingdata/metal/SubStructTableTest.java +++ b/core/src/test/java/io/parsingdata/metal/SubStructTableTest.java @@ -22,6 +22,7 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.def; import static io.parsingdata.metal.Shorthand.eq; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.ref; import static io.parsingdata.metal.Shorthand.repn; import static io.parsingdata.metal.Shorthand.seq; @@ -46,7 +47,7 @@ public class SubStructTableTest { private final Token table = seq(def("tableSize", con(1)), - repn(def("pointer", con(1)), ref("tableSize")), + repn(def("pointer", con(1)), last(ref("tableSize"))), sub(struct, ref("pointer"))); @Test diff --git a/core/src/test/java/io/parsingdata/metal/ToStringTest.java b/core/src/test/java/io/parsingdata/metal/ToStringTest.java index d0cf3379..269c8809 100644 --- a/core/src/test/java/io/parsingdata/metal/ToStringTest.java +++ b/core/src/test/java/io/parsingdata/metal/ToStringTest.java @@ -107,7 +107,7 @@ public void before() { @Test public void validateToStringImplementation() { final Expression e = not(and(eq(v(), v()), or(eqNum(v()), and(eqStr(v()), or(gtEqNum(v()), or(gtNum(v()), or(ltEqNum(v()), ltNum(v())))))))); - final Token t = until("untilName", v(), v(), v(), post(repn(sub(opt(pre(rep(cho(token("refName"), any(n()), seq(nod(10), tie(def(n(), last(v())), v()), whl(def(n(), con(1), e), e), tie(t(), con(1))))), e)), v()), v()), e)); + final Token t = until("untilName", v(), v(), v(), post(repn(sub(opt(pre(rep(cho(token("refName"), any(n()), seq(nod(10), tie(def(n(), last(v())), v()), whl(def(n(), con(1), e), e), tie(t(), con(1))))), e)), v()), last(v())), e)); final String output = t.toString(); for (int i = 0; i < count; i++) { assertTrue(output.contains(prefix + i)); diff --git a/core/src/test/java/io/parsingdata/metal/expression/value/NthExpressionTest.java b/core/src/test/java/io/parsingdata/metal/expression/value/NthExpressionTest.java index cdf6b865..9693d618 100644 --- a/core/src/test/java/io/parsingdata/metal/expression/value/NthExpressionTest.java +++ b/core/src/test/java/io/parsingdata/metal/expression/value/NthExpressionTest.java @@ -21,6 +21,7 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.div; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.nth; import static io.parsingdata.metal.Shorthand.ref; import static io.parsingdata.metal.Shorthand.repn; @@ -47,11 +48,11 @@ public class NthExpressionTest { any("valueCount"), repn( any("value"), - ref("valueCount")), + last(ref("valueCount"))), any("indexCount"), repn( any("index"), - ref("indexCount"))); + last(ref("indexCount")))); private final ValueExpression nth = nth(ref("value"), ref("index")); From b74a1117d4d99c2e4ba6bde34f4544e68eb970f4 Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Mon, 11 Feb 2019 22:50:53 +0100 Subject: [PATCH 3/8] #70: Converted Expand, Count, CurrentIteration, CurrentOffset, First and Self to SingleValueExpression. --- .../java/io/parsingdata/metal/Shorthand.java | 14 +++++----- .../metal/expression/value/Expand.java | 26 ++++++++++--------- .../expression/value/reference/Count.java | 11 ++++---- .../value/reference/CurrentIteration.java | 13 +++------- .../value/reference/CurrentOffset.java | 13 +++++----- .../expression/value/reference/First.java | 10 ++++--- .../metal/expression/value/reference/Nth.java | 7 +++-- .../expression/value/reference/Self.java | 13 +++++----- .../java/io/parsingdata/metal/ErrorsTest.java | 6 ----- .../metal/expression/value/ExpandTest.java | 13 +++++----- 10 files changed, 61 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index 313df7ea..044d05b1 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -92,8 +92,8 @@ 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(con(0)); + public static final SingleValueExpression CURRENT_OFFSET = new CurrentOffset(); + public static final SingleValueExpression CURRENT_ITERATION = new CurrentIteration(con(0)); public static final Expression TRUE = new True(); private Shorthand() {} @@ -187,18 +187,18 @@ private Shorthand() {} public static NameRef ref(final String name, final ValueExpression limit) { return new NameRef(name, limit); } public static DefinitionRef ref(final Token definition) { return ref(definition, null); } public static DefinitionRef ref(final Token definition, final ValueExpression limit) { return new DefinitionRef(definition, limit); } - public static ValueExpression first(final ValueExpression operand) { return new First(operand); } + public static SingleValueExpression first(final ValueExpression operand) { return new First(operand); } public static SingleValueExpression last(final ValueExpression operand) { return new Last(operand); } public static SingleValueExpression last(final NameRef operand) { return new Last(new NameRef(operand.reference, con(1))); } public static SingleValueExpression last(final DefinitionRef operand) { return new Last(new DefinitionRef(operand.reference, con(1))); } 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 ValueExpression iteration(final int level) { return iteration(con(level)); } - public static ValueExpression iteration(final ValueExpression level) { return new CurrentIteration(level); } + public static SingleValueExpression iteration(final int level) { return iteration(con(level)); } + public static SingleValueExpression iteration(final ValueExpression level) { return new CurrentIteration(level); } public static ValueExpression cat(final ValueExpression left, final ValueExpression right) { return new Cat(left, right); } public static ValueExpression cat(final ValueExpression operand) { return new FoldCat(operand); } public static ValueExpression elvis(final ValueExpression left, final ValueExpression right) { return new Elvis(left, right); } - public static ValueExpression count(final ValueExpression operand) { return new Count(operand); } + public static SingleValueExpression count(final ValueExpression operand) { return new Count(operand); } public static ValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer) { return new FoldLeft(values, reducer, null); } public static ValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer, final ValueExpression initial) { return new FoldLeft(values, reducer, initial); } public static ValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer) { return new FoldRight(values, reducer, null); } @@ -206,7 +206,7 @@ private Shorthand() {} public static ValueExpression fold(final ValueExpression values, final BinaryOperator reducer) { return foldRight(values, reducer); } public static ValueExpression fold(final ValueExpression values, final BinaryOperator reducer, final ValueExpression initial) { return foldRight(values, reducer, initial); } public static ValueExpression rev(final ValueExpression values) { return new Reverse(values); } - public static ValueExpression exp(final ValueExpression base, final ValueExpression count) { return new Expand(base, count); } + public static ValueExpression exp(final ValueExpression base, final SingleValueExpression count) { return new Expand(base, count); } public static BinaryValueExpression mapLeft(final BiFunction func, final ValueExpression left, final ValueExpression rightExpand) { return func.apply(left, exp(rightExpand, count(left))); } public static BinaryValueExpression mapRight(final BiFunction func, final ValueExpression leftExpand, final ValueExpression right) { return func.apply(exp(leftExpand, count(right)), right); } public static ValueExpression bytes(final ValueExpression operand) { return new Bytes(operand); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Expand.java b/core/src/main/java/io/parsingdata/metal/expression/value/Expand.java index 3d7c9888..1e9b5ebd 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Expand.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Expand.java @@ -22,6 +22,7 @@ import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; +import java.util.Optional; import io.parsingdata.metal.Trampoline; import io.parsingdata.metal.Util; @@ -33,19 +34,20 @@ * A {@link ValueExpression} that expands a result by copying and concatenating * it a specified amount of times. *

- * An Expand expression has two operands: bases and - * count (both {@link ValueExpression}s). Both operands are - * evaluated. An IllegalStateException is thrown if evaluating - * count yields more than a single value. Multiple copies of the - * result of evaluating bases are concatenated. The amount of copies - * equals the result of evaluating count. + * An Expand expression has two operands: bases (a + * {@link ValueExpression}) and count (a + * {@link SingleValueExpression}). Both operands are evaluated. Multiple copies + * of the result of evaluating bases are concatenated. The amount + * of copies equals the result of evaluating count. If + * count evaluated to an empty value or NOT_A_VALUE, + * an IllegalArgumentException is thrown. */ public class Expand implements ValueExpression { public final ValueExpression bases; - public final ValueExpression count; + public final SingleValueExpression count; - public Expand(final ValueExpression bases, final ValueExpression count) { + public Expand(final ValueExpression bases, final SingleValueExpression count) { this.bases = checkNotNull(bases, "bases"); this.count = checkNotNull(count, "count"); } @@ -56,11 +58,11 @@ public ImmutableList eval(final ParseState parseState, final Encoding enc if (baseList.isEmpty()) { return baseList; } - final ImmutableList countList = count.eval(parseState, encoding); - if (countList.size != 1 || countList.head.equals(NOT_A_VALUE)) { - throw new IllegalArgumentException("Count must evaluate to a single non-empty value."); + final Optional countValue = count.evalSingle(parseState, encoding); + if (!countValue.isPresent() || countValue.get().equals(NOT_A_VALUE)) { + throw new IllegalArgumentException("Count must evaluate to a non-empty countable value."); } - return expand(baseList, countList.head.asNumeric().intValueExact(), new ImmutableList<>()).computeResult(); + return expand(baseList, countValue.get().asNumeric().intValueExact(), new ImmutableList<>()).computeResult(); } private Trampoline> expand(final ImmutableList baseList, final int countValue, final ImmutableList aggregate) { diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Count.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Count.java index 7011ef13..0ab60ff2 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Count.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Count.java @@ -19,21 +19,22 @@ import static io.parsingdata.metal.Util.checkNotNull; import java.util.Objects; +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.encoding.Sign; import io.parsingdata.metal.expression.value.ConstantFactory; +import io.parsingdata.metal.expression.value.SingleValueExpression; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; /** - * A {@link ValueExpression} that represents the amount of {@link Value}s + * A {@link SingleValueExpression} that represents the amount of {@link Value}s * returned by evaluating its operand. */ -public class Count implements ValueExpression { +public class Count implements SingleValueExpression { public final ValueExpression operand; @@ -42,8 +43,8 @@ public Count(final ValueExpression operand) { } @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { - return ImmutableList.create(fromNumeric(operand.eval(parseState, encoding).size)); + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { + return Optional.of(fromNumeric(operand.eval(parseState, encoding).size)); } private static Value fromNumeric(final long length) { 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 61083953..867f0318 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 @@ -36,6 +36,7 @@ import io.parsingdata.metal.data.ImmutablePair; import io.parsingdata.metal.data.ParseState; 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; @@ -44,11 +45,11 @@ import io.parsingdata.metal.token.While; /** - * A {@link ValueExpression} that represents the 0-based current iteration in an + * 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}). */ -public class CurrentIteration implements ValueExpression { +public class CurrentIteration implements SingleValueExpression { private final ValueExpression level; @@ -57,13 +58,7 @@ public CurrentIteration(final ValueExpression level) { } @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { - return getIteration(parseState, encoding) - .map(ImmutableList::create) - .orElseGet(ImmutableList::new); - } - - private Optional getIteration(final ParseState parseState, final Encoding encoding) { + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { final BigInteger levelValue = getLevel(parseState, encoding); if (parseState.iterations.size <= levelValue.longValue()) { return Optional.empty(); diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/CurrentOffset.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/CurrentOffset.java index 4d291fb8..8b49f148 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/CurrentOffset.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/CurrentOffset.java @@ -19,22 +19,23 @@ import static io.parsingdata.metal.encoding.Encoding.DEFAULT_ENCODING; 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.SingleValueExpression; import io.parsingdata.metal.expression.value.Value; -import io.parsingdata.metal.expression.value.ValueExpression; /** - * A {@link ValueExpression} that represents the current offset in the + * A {@link SingleValueExpression} that represents the current offset in the * {@link ParseState}. */ -public class CurrentOffset implements ValueExpression { +public class CurrentOffset implements SingleValueExpression { @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { - return ImmutableList.create(createFromNumeric(parseState.offset, DEFAULT_ENCODING)); + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { + return Optional.of(createFromNumeric(parseState.offset, DEFAULT_ENCODING)); } @Override diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/First.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/First.java index af1b02bc..16e97322 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/First.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/First.java @@ -21,20 +21,22 @@ import static io.parsingdata.metal.Util.checkNotNull; import java.util.Objects; +import java.util.Optional; import io.parsingdata.metal.Trampoline; 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.SingleValueExpression; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; /** - * A {@link ValueExpression} that represents the first {@link Value} returned + * A {@link SingleValueExpression} that represents the first {@link Value} returned * by evaluating its operand. */ -public class First implements ValueExpression { +public class First implements SingleValueExpression { public final ValueExpression operand; @@ -43,9 +45,9 @@ public First(final ValueExpression operand) { } @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { final ImmutableList values = operand.eval(parseState, encoding); - return values.isEmpty() ? values : ImmutableList.create(getFirst(values).computeResult()); + return values.isEmpty() ? Optional.empty() : Optional.of(getFirst(values).computeResult()); } private Trampoline getFirst(final ImmutableList values) { diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Nth.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Nth.java index 77b0a070..e9f5ff41 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Nth.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Nth.java @@ -27,7 +27,6 @@ import java.math.BigInteger; import java.util.Objects; -import java.util.Optional; import io.parsingdata.metal.Trampoline; import io.parsingdata.metal.Util; @@ -44,9 +43,9 @@ * indices (both {@link ValueExpression}s). Both operands are * evaluated. Next, the resulting values of evaluating indices is * used as a list of integer indices into the results of evaluating - * values. For every invalid index (such as - * {@link Optional#empty()}, a negative value or an index that is out of - * bounds) empty is returned. + * values. For every invalid index (NOT_A_VALUE, a + * negative value or an index that is out of bounds) NOT_A_VALUE + * is returned. */ public class Nth implements ValueExpression { diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Self.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Self.java index d6ce920c..8d5daca9 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Self.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Self.java @@ -16,22 +16,23 @@ package io.parsingdata.metal.expression.value.reference; +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.SingleValueExpression; import io.parsingdata.metal.expression.value.Value; -import io.parsingdata.metal.expression.value.ValueExpression; /** - * A {@link ValueExpression} that represents the {@link Value} most recently + * A {@link SingleValueExpression} that represents the {@link Value} most recently * added to the parse state. */ -public class Self implements ValueExpression { +public class Self implements SingleValueExpression { @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { - return parseState.order.current().map(ImmutableList::create).orElseGet(ImmutableList::new); + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { + return parseState.order.current().flatMap(Optional::of); } @Override diff --git a/core/src/test/java/io/parsingdata/metal/ErrorsTest.java b/core/src/test/java/io/parsingdata/metal/ErrorsTest.java index f9866cf1..d73c43b5 100644 --- a/core/src/test/java/io/parsingdata/metal/ErrorsTest.java +++ b/core/src/test/java/io/parsingdata/metal/ErrorsTest.java @@ -26,23 +26,17 @@ import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.mul; import static io.parsingdata.metal.Shorthand.neg; -import static io.parsingdata.metal.Shorthand.ref; -import static io.parsingdata.metal.Shorthand.repn; -import static io.parsingdata.metal.Shorthand.seq; import static io.parsingdata.metal.Shorthand.sub; import static io.parsingdata.metal.data.ParseState.createFromByteStream; 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.math.BigInteger; -import java.util.Optional; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import io.parsingdata.metal.data.ParseState; import io.parsingdata.metal.token.Token; public class ErrorsTest { diff --git a/core/src/test/java/io/parsingdata/metal/expression/value/ExpandTest.java b/core/src/test/java/io/parsingdata/metal/expression/value/ExpandTest.java index f9b8bbee..c14833ee 100644 --- a/core/src/test/java/io/parsingdata/metal/expression/value/ExpandTest.java +++ b/core/src/test/java/io/parsingdata/metal/expression/value/ExpandTest.java @@ -23,6 +23,7 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.div; import static io.parsingdata.metal.Shorthand.exp; +import static io.parsingdata.metal.Shorthand.last; import static io.parsingdata.metal.Shorthand.ref; import static io.parsingdata.metal.data.ParseState.createFromByteStream; import static io.parsingdata.metal.data.Slice.createFromBytes; @@ -55,17 +56,17 @@ public void expandEmpty() { } @Test - public void expandEmptyTimes() { + public void expandNotAValueTimes() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Count must evaluate to a single non-empty value."); - exp(con(1), div(con(1), con(0))).eval(EMPTY_PARSE_STATE, enc()); + thrown.expectMessage("Count must evaluate to a non-empty countable value."); + exp(con(1), last(div(con(1), con(0)))).eval(EMPTY_PARSE_STATE, enc()); } @Test - public void expandListTimes() { + public void expandEmptyTimes() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Count must evaluate to a single non-empty value."); - exp(con(1), ref("a")).eval(createFromByteStream(DUMMY_STREAM).add(PARSEVALUE_1).add(PARSEVALUE_2), enc()); + thrown.expectMessage("Count must evaluate to a non-empty countable value."); + exp(con(1), last(ref("a"))).eval(EMPTY_PARSE_STATE, enc()); } @Test From d7543b44db6c90f286e6c72e8e09f3da8ee96283 Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Mon, 11 Feb 2019 23:25:21 +0100 Subject: [PATCH 4/8] #70: Converted the initial argument to the various Fold ValueExpressions to SingleValueExpression. --- .../java/io/parsingdata/metal/Shorthand.java | 6 ++--- .../metal/expression/value/Fold.java | 17 +++++++------- .../metal/expression/value/FoldLeft.java | 2 +- .../metal/expression/value/FoldRight.java | 2 +- .../io/parsingdata/metal/ToStringTest.java | 2 +- .../expression/value/FoldEdgeCaseTest.java | 23 ------------------- 6 files changed, 15 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index 044d05b1..d195a552 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -200,11 +200,11 @@ private Shorthand() {} public static ValueExpression elvis(final ValueExpression left, final ValueExpression right) { return new Elvis(left, right); } public static SingleValueExpression count(final ValueExpression operand) { return new Count(operand); } public static ValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer) { return new FoldLeft(values, reducer, null); } - public static ValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer, final ValueExpression initial) { return new FoldLeft(values, reducer, initial); } + public static ValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return new FoldLeft(values, reducer, initial); } public static ValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer) { return new FoldRight(values, reducer, null); } - public static ValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer, final ValueExpression initial) { return new FoldRight(values, reducer, initial); } + public static ValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return new FoldRight(values, reducer, initial); } public static ValueExpression fold(final ValueExpression values, final BinaryOperator reducer) { return foldRight(values, reducer); } - public static ValueExpression fold(final ValueExpression values, final BinaryOperator reducer, final ValueExpression initial) { return foldRight(values, reducer, initial); } + public static ValueExpression fold(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return foldRight(values, reducer, initial); } public static ValueExpression rev(final ValueExpression values) { return new Reverse(values); } public static ValueExpression exp(final ValueExpression base, final SingleValueExpression count) { return new Expand(base, count); } public static BinaryValueExpression mapLeft(final BiFunction func, final ValueExpression left, final ValueExpression rightExpand) { return func.apply(left, exp(rightExpand, count(left))); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java b/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java index 61bdf78d..8e58fd4b 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java @@ -22,6 +22,7 @@ import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; +import java.util.Optional; import java.util.function.BinaryOperator; import io.parsingdata.metal.Trampoline; @@ -36,8 +37,8 @@ *

* Fold has three operands: values (a {@link ValueExpression}), * reducer (a {@link BinaryOperator}) and initial (a - * {@link ValueExpression}). First initial is evaluated. If it - * does not return a single value, the final result is an empty list. Next, + * {@link SingleValueExpression}). First initial is evaluated. If it + * does not return a valid value, the final result is an empty list. Next, * values is evaluated and its result is passed to the abstract * {@link #prepareValues(ImmutableList)} method. The returned list is prefixed * by the value returned by evaluating initial. On this list, the @@ -48,9 +49,9 @@ public abstract class Fold implements ValueExpression { public final ValueExpression values; public final BinaryOperator reducer; - public final ValueExpression initial; + public final SingleValueExpression initial; - public Fold(final ValueExpression values, final BinaryOperator reducer, final ValueExpression initial) { + public Fold(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { this.values = checkNotNull(values, "values"); this.reducer = checkNotNull(reducer, "reducer"); this.initial = initial; @@ -58,18 +59,18 @@ public Fold(final ValueExpression values, final BinaryOperator @Override public ImmutableList eval(final ParseState parseState, final Encoding encoding) { - final ImmutableList initialList = initial != null ? initial.eval(parseState, encoding) : new ImmutableList<>(); - if (initialList.size > 1 || (!initialList.isEmpty() && initialList.head.equals(NOT_A_VALUE))) { + final Optional initialValue = initial != null ? initial.evalSingle(parseState, encoding) : Optional.empty(); + if (initialValue.isPresent() && initialValue.get().equals(NOT_A_VALUE)) { return ImmutableList.create(NOT_A_VALUE); } final ImmutableList unpreparedValues = this.values.eval(parseState, encoding); if (unpreparedValues.isEmpty()) { - return initialList; + return initialValue.map(ImmutableList::create).orElseGet(ImmutableList::new); } if (containsNotAValue(unpreparedValues).computeResult()) { return ImmutableList.create(NOT_A_VALUE); } - final ImmutableList valueList = prepareValues(unpreparedValues).add(initialList); + final ImmutableList valueList = initialValue.map(value -> prepareValues(unpreparedValues).add(value)).orElseGet(() -> prepareValues(unpreparedValues)); return ImmutableList.create(fold(parseState, encoding, reducer, valueList.head, valueList.tail).computeResult()); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/FoldLeft.java b/core/src/main/java/io/parsingdata/metal/expression/value/FoldLeft.java index a8d54984..d4f61e53 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/FoldLeft.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/FoldLeft.java @@ -33,7 +33,7 @@ */ public class FoldLeft extends Fold { - public FoldLeft(final ValueExpression values, final BinaryOperator reducer, final ValueExpression initial) { + public FoldLeft(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { super(values, reducer, initial); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/FoldRight.java b/core/src/main/java/io/parsingdata/metal/expression/value/FoldRight.java index 7c2267ff..f11f7f25 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/FoldRight.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/FoldRight.java @@ -32,7 +32,7 @@ */ public class FoldRight extends Fold { - public FoldRight(final ValueExpression values, final BinaryOperator reducer, final ValueExpression initial) { + public FoldRight(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { super(values, reducer, initial); } diff --git a/core/src/test/java/io/parsingdata/metal/ToStringTest.java b/core/src/test/java/io/parsingdata/metal/ToStringTest.java index 269c8809..76853926 100644 --- a/core/src/test/java/io/parsingdata/metal/ToStringTest.java +++ b/core/src/test/java/io/parsingdata/metal/ToStringTest.java @@ -124,7 +124,7 @@ private Token t() { } private ValueExpression v() { - return fold(foldLeft(foldRight(rev(bytes(neg(add(div(mod(mul(sub(cat(last(ref(n()))), first(nth(exp(ref(n()), con(NOT_A_VALUE)), con(1)))), sub(CURRENT_ITERATION, con(1))), cat(ref(n()), ref(t()))), add(SELF, add(offset(ref(n())), add(CURRENT_OFFSET, count(ref(n())))))), elvis(ref(n()), ref(n())))))), Shorthand::add, ref(n())), Shorthand::add), Shorthand::add, ref(n())); + return fold(foldLeft(foldRight(rev(bytes(neg(add(div(mod(mul(sub(cat(last(ref(n()))), first(nth(exp(ref(n()), con(NOT_A_VALUE)), con(1)))), sub(CURRENT_ITERATION, con(1))), cat(ref(n()), ref(t()))), add(SELF, add(offset(ref(n())), add(CURRENT_OFFSET, count(ref(n())))))), elvis(ref(n()), ref(n())))))), Shorthand::add, last(ref(n()))), Shorthand::add), Shorthand::add, last(ref(n()))); } @Test diff --git a/core/src/test/java/io/parsingdata/metal/expression/value/FoldEdgeCaseTest.java b/core/src/test/java/io/parsingdata/metal/expression/value/FoldEdgeCaseTest.java index 5ede5dc7..eb31df30 100644 --- a/core/src/test/java/io/parsingdata/metal/expression/value/FoldEdgeCaseTest.java +++ b/core/src/test/java/io/parsingdata/metal/expression/value/FoldEdgeCaseTest.java @@ -83,29 +83,6 @@ public void inputContainsEmptyInTail() { assertEquals(NOT_A_VALUE, foldRight((parseState, encoding) -> ImmutableList.create(NOT_A_VALUE).add(new CoreValue(createFromBytes(new byte[] { 1, 2 }), enc())), Shorthand::add).eval(stream(0), enc()).head); } - @Test - public void multipleInits() { - final Optional parseResult = - seq( - def("init", 1), - def("init", 1), - def("toFold", 1), - def("toFold", 1), - cho( - def("folded", 1, eq(foldLeft(ref("toFold"), Shorthand::add, ref("init")))), - def("folded", 1, eq(foldRight(ref("toFold"), Shorthand::add, ref("init")))) - ) - ).parse(env(stream(1, 2, 1, 2, 3))); - assertFalse(parseResult.isPresent()); - } - - @Test - public void twoInits() { - final ImmutableList result = fold(exp(con(1), con(2)), Shorthand::add, exp(con(1), con(2))).eval(EMPTY_PARSE_STATE, DEFAULT_ENCODING); - assertEquals(1, result.size); - assertEquals(NOT_A_VALUE, result.head); - } - @Test public void notAValueInit() { final ImmutableList result = fold(exp(con(1), con(2)), Shorthand::add, con(NOT_A_VALUE)).eval(EMPTY_PARSE_STATE, DEFAULT_ENCODING); From b7313013b0111b2830c51bd2e3560137b1846884 Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Mon, 11 Feb 2019 23:45:21 +0100 Subject: [PATCH 5/8] #70: Converted type of SELF in Shorthand. --- core/src/main/java/io/parsingdata/metal/Shorthand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index d195a552..fc5cd138 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -91,7 +91,7 @@ public final class Shorthand { public static final Token EMPTY = def(EMPTY_NAME, 0L); - public static final ValueExpression SELF = new Self(); + public static final SingleValueExpression SELF = new Self(); public static final SingleValueExpression CURRENT_OFFSET = new CurrentOffset(); public static final SingleValueExpression CURRENT_ITERATION = new CurrentIteration(con(0)); public static final Expression TRUE = new True(); From a6909895175b5592967418a2c46d30f79646844a Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Tue, 12 Feb 2019 11:55:23 +0100 Subject: [PATCH 6/8] #70: Resolved variable name shadowing. --- core/src/main/java/io/parsingdata/metal/token/Def.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/parsingdata/metal/token/Def.java b/core/src/main/java/io/parsingdata/metal/token/Def.java index 2fddf46e..72f3b922 100644 --- a/core/src/main/java/io/parsingdata/metal/token/Def.java +++ b/core/src/main/java/io/parsingdata/metal/token/Def.java @@ -56,8 +56,8 @@ public Def(final String name, final SingleValueExpression size, final Encoding e @Override protected Optional parseImpl(final Environment environment) { return size.evalSingle(environment.parseState, environment.encoding) - .filter(size -> !size.equals(NOT_A_VALUE)) - .flatMap(size -> size.asNumeric().compareTo(ZERO) != 0 ? slice(environment, size.asNumeric()) : success(environment.parseState)); + .filter(sizeValue -> !sizeValue.equals(NOT_A_VALUE)) + .flatMap(sizeValue -> sizeValue.asNumeric().compareTo(ZERO) != 0 ? slice(environment, sizeValue.asNumeric()) : success(environment.parseState)); } private Optional slice(final Environment environment, final BigInteger dataSize) { From 3deb45102a6113af615e9997eb7c7b46a0087ae5 Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Tue, 12 Feb 2019 12:16:45 +0100 Subject: [PATCH 7/8] #70: Converted all Fold operations to SingleValueExpression. --- .../main/java/io/parsingdata/metal/Shorthand.java | 14 +++++++------- .../parsingdata/metal/expression/value/Fold.java | 14 +++++++------- .../metal/expression/value/FoldCat.java | 11 +++++------ .../metal/expression/value/FoldLeft.java | 2 +- .../metal/expression/value/FoldRight.java | 2 +- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index fc5cd138..fc42c9ca 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -196,15 +196,15 @@ private Shorthand() {} public static SingleValueExpression iteration(final int level) { return iteration(con(level)); } public static SingleValueExpression iteration(final ValueExpression level) { return new CurrentIteration(level); } public static ValueExpression cat(final ValueExpression left, final ValueExpression right) { return new Cat(left, right); } - public static ValueExpression cat(final ValueExpression operand) { return new FoldCat(operand); } + 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); } public static SingleValueExpression count(final ValueExpression operand) { return new Count(operand); } - public static ValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer) { return new FoldLeft(values, reducer, null); } - public static ValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return new FoldLeft(values, reducer, initial); } - public static ValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer) { return new FoldRight(values, reducer, null); } - public static ValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return new FoldRight(values, reducer, initial); } - public static ValueExpression fold(final ValueExpression values, final BinaryOperator reducer) { return foldRight(values, reducer); } - public static ValueExpression fold(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return foldRight(values, reducer, initial); } + public static SingleValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer) { return new FoldLeft(values, reducer, null); } + public static SingleValueExpression foldLeft(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return new FoldLeft(values, reducer, initial); } + public static SingleValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer) { return new FoldRight(values, reducer, null); } + public static SingleValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return new FoldRight(values, reducer, initial); } + public static SingleValueExpression fold(final ValueExpression values, final BinaryOperator reducer) { return foldRight(values, reducer); } + public static SingleValueExpression fold(final ValueExpression values, final BinaryOperator reducer, final SingleValueExpression initial) { return foldRight(values, reducer, initial); } public static ValueExpression rev(final ValueExpression values) { return new Reverse(values); } public static ValueExpression exp(final ValueExpression base, final SingleValueExpression count) { return new Expand(base, count); } public static BinaryValueExpression mapLeft(final BiFunction func, final ValueExpression left, final ValueExpression rightExpand) { return func.apply(left, exp(rightExpand, count(left))); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java b/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java index 8e58fd4b..4a0ae206 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java @@ -32,7 +32,7 @@ import io.parsingdata.metal.encoding.Encoding; /** - * Base class for {@link ValueExpression} implementations of the Fold + * Base class for {@link SingleValueExpression} implementations of the Fold * operation. *

* Fold has three operands: values (a {@link ValueExpression}), @@ -45,7 +45,7 @@ * reducer is applied to the first two values until a single * value remains, which is then returned. */ -public abstract class Fold implements ValueExpression { +public abstract class Fold implements SingleValueExpression { public final ValueExpression values; public final BinaryOperator reducer; @@ -58,20 +58,20 @@ public Fold(final ValueExpression values, final BinaryOperator } @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { final Optional initialValue = initial != null ? initial.evalSingle(parseState, encoding) : Optional.empty(); if (initialValue.isPresent() && initialValue.get().equals(NOT_A_VALUE)) { - return ImmutableList.create(NOT_A_VALUE); + return initialValue; } final ImmutableList unpreparedValues = this.values.eval(parseState, encoding); if (unpreparedValues.isEmpty()) { - return initialValue.map(ImmutableList::create).orElseGet(ImmutableList::new); + return initialValue; } if (containsNotAValue(unpreparedValues).computeResult()) { - return ImmutableList.create(NOT_A_VALUE); + return Optional.of(NOT_A_VALUE); } final ImmutableList valueList = initialValue.map(value -> prepareValues(unpreparedValues).add(value)).orElseGet(() -> prepareValues(unpreparedValues)); - return ImmutableList.create(fold(parseState, encoding, reducer, valueList.head, valueList.tail).computeResult()); + return Optional.of(fold(parseState, encoding, reducer, valueList.head, valueList.tail).computeResult()); } private Trampoline fold(final ParseState parseState, final Encoding encoding, final BinaryOperator reducer, final Value head, final ImmutableList tail) { diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/FoldCat.java b/core/src/main/java/io/parsingdata/metal/expression/value/FoldCat.java index ed1fca5d..f7c9449a 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/FoldCat.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/FoldCat.java @@ -21,21 +21,21 @@ import static io.parsingdata.metal.data.Slice.createFromSource; import java.util.Objects; +import java.util.Optional; import io.parsingdata.metal.Util; import io.parsingdata.metal.data.ConcatenatedValueSource; -import io.parsingdata.metal.data.ImmutableList; import io.parsingdata.metal.data.ParseState; import io.parsingdata.metal.encoding.Encoding; /** - * A {@link ValueExpression} that represents an optimized version of a + * A {@link SingleValueExpression} that represents an optimized version of a * {@link FoldLeft} operation with a {@link Cat} ValueExpression as reducer. * * @see FoldLeft * @see Cat */ -public class FoldCat implements ValueExpression { +public class FoldCat implements SingleValueExpression { public final ValueExpression operand; @@ -44,11 +44,10 @@ public FoldCat(final ValueExpression operand) { } @Override - public ImmutableList eval(final ParseState parseState, final Encoding encoding) { + public Optional evalSingle(final ParseState parseState, final Encoding encoding) { return ConcatenatedValueSource.create(operand.eval(parseState, encoding)) .flatMap(source -> createFromSource(source, ZERO, source.length)) - .map(slice -> ImmutableList.create(new CoreValue(slice, encoding))) - .orElseGet(ImmutableList::new); + .map(slice -> new CoreValue(slice, encoding)); } @Override diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/FoldLeft.java b/core/src/main/java/io/parsingdata/metal/expression/value/FoldLeft.java index d4f61e53..56300e2a 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/FoldLeft.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/FoldLeft.java @@ -24,7 +24,7 @@ import io.parsingdata.metal.data.ImmutableList; /** - * A {@link ValueExpression} implementation of the FoldLeft operation. + * A {@link SingleValueExpression} implementation of the FoldLeft operation. *

* FoldLeft differs from {@link FoldRight} in that the reduce operation is * applied from left to right (i.e., starting at the top). diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/FoldRight.java b/core/src/main/java/io/parsingdata/metal/expression/value/FoldRight.java index f11f7f25..7804ea9f 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/FoldRight.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/FoldRight.java @@ -23,7 +23,7 @@ import io.parsingdata.metal.data.ImmutableList; /** - * A {@link ValueExpression} implementation of the FoldRight operation. + * A {@link SingleValueExpression} implementation of the FoldRight operation. *

* FoldRight differs from {@link FoldLeft} in that the reduce operation is * applied from right to left (i.e., starting at the bottom). From 60fedd2aab3757db45da3bfe97566f2016cf1954 Mon Sep 17 00:00:00 2001 From: Jeroen van den Bos Date: Sat, 16 Feb 2019 16:33:47 +0100 Subject: [PATCH 8/8] #70: Handled review comments from @rdvdijk and @ccreeten. --- .../parsingdata/metal/expression/value/Expand.java | 12 +++++------- .../expression/value/SingleValueExpression.java | 2 +- .../metal/expression/value/reference/Nth.java | 5 +++-- .../metal/expression/value/reference/Self.java | 4 +++- .../main/java/io/parsingdata/metal/token/RepN.java | 2 +- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Expand.java b/core/src/main/java/io/parsingdata/metal/expression/value/Expand.java index 1e9b5ebd..34fff92d 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Expand.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Expand.java @@ -22,7 +22,6 @@ import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; -import java.util.Optional; import io.parsingdata.metal.Trampoline; import io.parsingdata.metal.Util; @@ -40,7 +39,7 @@ * of the result of evaluating bases are concatenated. The amount * of copies equals the result of evaluating count. If * count evaluated to an empty value or NOT_A_VALUE, - * an IllegalArgumentException is thrown. + * an {@link IllegalArgumentException} is thrown. */ public class Expand implements ValueExpression { @@ -58,11 +57,10 @@ public ImmutableList eval(final ParseState parseState, final Encoding enc if (baseList.isEmpty()) { return baseList; } - final Optional countValue = count.evalSingle(parseState, encoding); - if (!countValue.isPresent() || countValue.get().equals(NOT_A_VALUE)) { - throw new IllegalArgumentException("Count must evaluate to a non-empty countable value."); - } - return expand(baseList, countValue.get().asNumeric().intValueExact(), new ImmutableList<>()).computeResult(); + return count.evalSingle(parseState, encoding) + .filter(countValue -> !countValue.equals(NOT_A_VALUE)) + .map(countValue -> expand(baseList, countValue.asNumeric().intValueExact(), new ImmutableList<>()).computeResult()) + .orElseThrow(() -> new IllegalArgumentException("Count must evaluate to a non-empty countable value.")); } private Trampoline> expand(final ImmutableList baseList, final int countValue, final ImmutableList aggregate) { diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/SingleValueExpression.java b/core/src/main/java/io/parsingdata/metal/expression/value/SingleValueExpression.java index 0bdfc5bd..51f67681 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/SingleValueExpression.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/SingleValueExpression.java @@ -26,7 +26,7 @@ * Interface for all SingleValueExpression implementations. *

* A SingleValueExpression is an expression that is evaluated by executing its - * {@link #eval(ParseState, Encoding)} method. It yields an {@link Optional} + * {@link #evalSingle(ParseState, Encoding)} method. It yields an {@link Optional} * {@link Value} object. *

* As context, it receives the current ParseState object as diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Nth.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Nth.java index e9f5ff41..51679a52 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Nth.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Nth.java @@ -33,6 +33,7 @@ import io.parsingdata.metal.data.ImmutableList; import io.parsingdata.metal.data.ParseState; import io.parsingdata.metal.encoding.Encoding; +import io.parsingdata.metal.expression.value.NotAValue; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; @@ -43,8 +44,8 @@ * indices (both {@link ValueExpression}s). Both operands are * evaluated. Next, the resulting values of evaluating indices is * used as a list of integer indices into the results of evaluating - * values. For every invalid index (NOT_A_VALUE, a - * negative value or an index that is out of bounds) NOT_A_VALUE + * values. For every invalid index ({@link NotAValue#NOT_A_VALUE}, a + * negative value or an index that is out of bounds) {@link NotAValue#NOT_A_VALUE} * is returned. */ public class Nth implements ValueExpression { diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Self.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Self.java index 8d5daca9..b4f5c392 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Self.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Self.java @@ -16,6 +16,8 @@ package io.parsingdata.metal.expression.value.reference; +import static java.util.function.Function.identity; + import java.util.Optional; import io.parsingdata.metal.Util; @@ -32,7 +34,7 @@ public class Self implements SingleValueExpression { @Override public Optional evalSingle(final ParseState parseState, final Encoding encoding) { - return parseState.order.current().flatMap(Optional::of); + return parseState.order.current().map(identity()); } @Override diff --git a/core/src/main/java/io/parsingdata/metal/token/RepN.java b/core/src/main/java/io/parsingdata/metal/token/RepN.java index 65ec83b9..482bfd63 100644 --- a/core/src/main/java/io/parsingdata/metal/token/RepN.java +++ b/core/src/main/java/io/parsingdata/metal/token/RepN.java @@ -55,7 +55,7 @@ protected Optional parseImpl(final Environment environment) { .filter(count -> !count.equals(NOT_A_VALUE)) .flatMap(count -> parse(environment, env -> env.parseState.iterations.head.right.compareTo(count.asNumeric()) >= 0, env -> failure())); } - + @Override public String toString() { return getClass().getSimpleName() + "(" + makeNameFragment() + token + "," + n + ")";