diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index 2e739c94..fc42c9ca 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -45,12 +45,14 @@ import io.parsingdata.metal.expression.value.Cat; import io.parsingdata.metal.expression.value.Const; import io.parsingdata.metal.expression.value.ConstantFactory; +import io.parsingdata.metal.expression.value.CoreValue; import io.parsingdata.metal.expression.value.Elvis; import io.parsingdata.metal.expression.value.Expand; import io.parsingdata.metal.expression.value.FoldCat; import io.parsingdata.metal.expression.value.FoldLeft; import io.parsingdata.metal.expression.value.FoldRight; import io.parsingdata.metal.expression.value.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; @@ -89,22 +91,22 @@ 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 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(); 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); } @@ -114,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); } @@ -171,40 +173,40 @@ 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 Value(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 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 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 ValueExpression 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); } - public static ValueExpression foldRight(final ValueExpression values, final BinaryOperator reducer, final ValueExpression 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 SingleValueExpression count(final ValueExpression operand) { return new Count(operand); } + 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 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/Util.java b/core/src/main/java/io/parsingdata/metal/Util.java index 96f4728a..b7fadfb0 100644 --- a/core/src/main/java/io/parsingdata/metal/Util.java +++ b/core/src/main/java/io/parsingdata/metal/Util.java @@ -28,6 +28,7 @@ import io.parsingdata.metal.data.ParseState; import io.parsingdata.metal.data.Slice; import io.parsingdata.metal.encoding.Encoding; +import io.parsingdata.metal.expression.value.CoreValue; import io.parsingdata.metal.expression.value.UnaryValueExpression; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; @@ -94,7 +95,7 @@ public static ValueExpression inflate(final ValueExpression target) { @Override public Optional eval(final Value value, final ParseState parseState, final Encoding encoding) { final Inflater inf = new Inflater(true); - inf.setInput(value.getValue()); + inf.setInput(value.value()); final byte[] dataReceiver = new byte[512]; final ByteArrayOutputStream out = new ByteArrayOutputStream(); while(!inf.finished()) { @@ -105,7 +106,7 @@ public Optional eval(final Value value, final ParseState parseState, fina return Optional.empty(); } } - return Optional.of(new Value(Slice.createFromBytes(out.toByteArray()), encoding)); + return Optional.of(new CoreValue(Slice.createFromBytes(out.toByteArray()), encoding)); } }; } diff --git a/core/src/main/java/io/parsingdata/metal/data/ConcatenatedValueSource.java b/core/src/main/java/io/parsingdata/metal/data/ConcatenatedValueSource.java index 8cb8cecc..d434b657 100644 --- a/core/src/main/java/io/parsingdata/metal/data/ConcatenatedValueSource.java +++ b/core/src/main/java/io/parsingdata/metal/data/ConcatenatedValueSource.java @@ -24,7 +24,7 @@ import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.Util.format; import static io.parsingdata.metal.data.Selection.reverse; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.math.BigInteger; import java.util.Objects; @@ -61,10 +61,10 @@ private static Trampoline calculateTotalSize(final ImmutableList size); } - if (values.head == NOT_A_VALUE) { + if (values.head.equals(NOT_A_VALUE)) { return complete(() -> ZERO); } - return intermediate(() -> calculateTotalSize(values.tail, size.add(values.head.slice.length))); + return intermediate(() -> calculateTotalSize(values.tail, size.add(values.head.slice().length))); } @Override @@ -79,13 +79,13 @@ private Trampoline getData(final ImmutableList values, final BigI if (length.compareTo(ZERO) <= 0) { return complete(() -> output); } - if (currentOffset.add(values.head.slice.length).compareTo(offset) <= 0) { - return intermediate(() -> getData(values.tail, currentOffset.add(values.head.slice.length), currentDest, offset, length, output)); + if (currentOffset.add(values.head.slice().length).compareTo(offset) <= 0) { + return intermediate(() -> getData(values.tail, currentOffset.add(values.head.slice().length), currentDest, offset, length, output)); } final BigInteger localOffset = offset.subtract(currentOffset).compareTo(ZERO) < 0 ? ZERO : offset.subtract(currentOffset); - final BigInteger toCopy = length.compareTo(values.head.slice.length.subtract(localOffset)) > 0 ? values.head.slice.length.subtract(localOffset) : length; - System.arraycopy(values.head.slice.getData(), localOffset.intValueExact(), output, currentDest.intValueExact(), toCopy.intValueExact()); - return intermediate(() -> getData(values.tail, currentOffset.add(values.head.slice.length), currentDest.add(toCopy), offset, length.subtract(toCopy), output)); + final BigInteger toCopy = length.compareTo(values.head.slice().length.subtract(localOffset)) > 0 ? values.head.slice().length.subtract(localOffset) : length; + System.arraycopy(values.head.slice().getData(), localOffset.intValueExact(), output, currentDest.intValueExact(), toCopy.intValueExact()); + return intermediate(() -> getData(values.tail, currentOffset.add(values.head.slice().length), currentDest.add(toCopy), offset, length.subtract(toCopy), output)); } @Override diff --git a/core/src/main/java/io/parsingdata/metal/data/DataExpressionSource.java b/core/src/main/java/io/parsingdata/metal/data/DataExpressionSource.java index 1c5750ad..4da10fd5 100644 --- a/core/src/main/java/io/parsingdata/metal/data/DataExpressionSource.java +++ b/core/src/main/java/io/parsingdata/metal/data/DataExpressionSource.java @@ -21,6 +21,7 @@ import static io.parsingdata.metal.Util.checkNotNegative; import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.Util.format; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.math.BigInteger; import java.util.Objects; @@ -71,10 +72,10 @@ private synchronized byte[] getValue() { throw new IllegalStateException(format("ValueExpression dataExpression must yield at least %d results.", index+1)); } final Value cacheValue = getValueAtIndex(results, index, 0).computeResult(); - if (cacheValue == Value.NOT_A_VALUE) { + if (cacheValue.equals(NOT_A_VALUE)) { throw new IllegalStateException(format("ValueExpression dataExpression yields empty Value at index %d.", index)); } - cache = cacheValue.getValue(); + cache = cacheValue.value(); } return cache; } diff --git a/core/src/main/java/io/parsingdata/metal/data/ParseItem.java b/core/src/main/java/io/parsingdata/metal/data/ParseItem.java index 7a95a39d..04479554 100644 --- a/core/src/main/java/io/parsingdata/metal/data/ParseItem.java +++ b/core/src/main/java/io/parsingdata/metal/data/ParseItem.java @@ -21,11 +21,17 @@ public interface ParseItem { default boolean isValue() { return false; } + default boolean isGraph() { return false; } + default boolean isReference() { return false; } + default ParseValue asValue() { throw new UnsupportedOperationException("Cannot convert to ParseValue."); } + default ParseReference asReference() { throw new UnsupportedOperationException("Cannot convert to ParseReference."); } + default ParseGraph asGraph() { throw new UnsupportedOperationException("Cannot convert to ParseGraph."); } + Token getDefinition(); } diff --git a/core/src/main/java/io/parsingdata/metal/data/ParseValue.java b/core/src/main/java/io/parsingdata/metal/data/ParseValue.java index 0f2ea7a6..2817bb87 100644 --- a/core/src/main/java/io/parsingdata/metal/data/ParseValue.java +++ b/core/src/main/java/io/parsingdata/metal/data/ParseValue.java @@ -22,10 +22,10 @@ import java.util.Objects; import io.parsingdata.metal.encoding.Encoding; -import io.parsingdata.metal.expression.value.Value; +import io.parsingdata.metal.expression.value.CoreValue; import io.parsingdata.metal.token.Token; -public class ParseValue extends Value implements ParseItem { +public class ParseValue extends CoreValue implements ParseItem { public final String name; public final Token definition; diff --git a/core/src/main/java/io/parsingdata/metal/data/Selection.java b/core/src/main/java/io/parsingdata/metal/data/Selection.java index 0e7c72fc..3d1557b9 100644 --- a/core/src/main/java/io/parsingdata/metal/data/Selection.java +++ b/core/src/main/java/io/parsingdata/metal/data/Selection.java @@ -57,7 +57,7 @@ public static Trampoline> findItemAtOffset(final ImmutableLi } private static boolean matchesLocation(final ParseValue value, final BigInteger offset, final Source source) { - return value.slice.offset.compareTo(offset) == 0 && value.slice.source.equals(source); + return value.slice().offset.compareTo(offset) == 0 && value.slice().source.equals(source); } private static Trampoline getLowestOffsetValue(final ImmutableList graphList, final ParseValue lowest) { @@ -77,7 +77,7 @@ private static ParseValue compareIfValue(final ParseValue lowest, final ParseIte } private static ParseValue getLowest(final ParseValue lowest, final ParseValue value) { - return lowest == null || lowest.slice.offset.compareTo(value.slice.offset) > 0 ? value : lowest; + return lowest == null || lowest.slice().offset.compareTo(value.slice().offset) > 0 ? value : lowest; } private static ImmutableList addIfGraph(final ImmutableList graphList, final ParseItem head) { diff --git a/core/src/main/java/io/parsingdata/metal/expression/comparison/ComparisonExpression.java b/core/src/main/java/io/parsingdata/metal/expression/comparison/ComparisonExpression.java index dda0913b..cfeebfab 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/comparison/ComparisonExpression.java +++ b/core/src/main/java/io/parsingdata/metal/expression/comparison/ComparisonExpression.java @@ -19,7 +19,7 @@ import static io.parsingdata.metal.Trampoline.complete; import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; @@ -68,7 +68,7 @@ public boolean eval(final ParseState parseState, final Encoding encoding) { } private Trampoline compare(final ImmutableList currents, final ImmutableList predicates) { - if (currents.head == NOT_A_VALUE || predicates.head == NOT_A_VALUE) { + if (currents.head.equals(NOT_A_VALUE) || predicates.head.equals(NOT_A_VALUE)) { return complete(() -> false); } final boolean headResult = compare(currents.head, predicates.head); diff --git a/core/src/main/java/io/parsingdata/metal/expression/comparison/Eq.java b/core/src/main/java/io/parsingdata/metal/expression/comparison/Eq.java index 331a5dee..ab4222ea 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/comparison/Eq.java +++ b/core/src/main/java/io/parsingdata/metal/expression/comparison/Eq.java @@ -40,8 +40,8 @@ public Eq(final ValueExpression value, final ValueExpression predicate) { @Override public boolean compare(final Value left, final Value right) { - return left.slice.length.compareTo(right.slice.length) == 0 - && Arrays.equals(left.getValue(), right.getValue()); + return left.slice().length.compareTo(right.slice().length) == 0 + && Arrays.equals(left.value(), right.value()); } } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/BinaryValueExpression.java b/core/src/main/java/io/parsingdata/metal/expression/value/BinaryValueExpression.java index 562bb51f..96691b67 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/BinaryValueExpression.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/BinaryValueExpression.java @@ -20,7 +20,7 @@ import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.data.Selection.reverse; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; import java.util.Optional; @@ -88,7 +88,7 @@ private Trampoline> padList(final ImmutableList list } private Value checkEval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) { - if (leftValue == NOT_A_VALUE || rightValue == NOT_A_VALUE) { + if (leftValue.equals(NOT_A_VALUE) || rightValue.equals(NOT_A_VALUE)) { return NOT_A_VALUE; } return eval(leftValue, rightValue, parseState, encoding).orElse(NOT_A_VALUE); diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Bytes.java b/core/src/main/java/io/parsingdata/metal/expression/value/Bytes.java index 8703a047..893cf8e1 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Bytes.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Bytes.java @@ -22,7 +22,7 @@ import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.data.Slice.createFromSource; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.math.BigInteger; import java.util.Objects; @@ -71,10 +71,10 @@ private Trampoline> toByteValues(final ImmutableList } private Trampoline> extractByteValues(final ImmutableList output, final Value value, final int i, final Encoding encoding) { - if (value == NOT_A_VALUE || BigInteger.valueOf(i).compareTo(value.getLength()) >= 0) { + if (value.equals(NOT_A_VALUE) || BigInteger.valueOf(i).compareTo(value.length()) >= 0) { return complete(() -> output); } - return intermediate(() -> extractByteValues(output.add(new Value(createFromSource(value.slice.source, value.slice.offset.add(BigInteger.valueOf(i)), ONE).get(), encoding)), value, i + 1, encoding)); + return intermediate(() -> extractByteValues(output.add(new CoreValue(createFromSource(value.slice().source, value.slice().offset.add(BigInteger.valueOf(i)), ONE).get(), encoding)), value, i + 1, encoding)); } @Override diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Cat.java b/core/src/main/java/io/parsingdata/metal/expression/value/Cat.java index 6f1972f5..47e41764 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Cat.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Cat.java @@ -39,8 +39,8 @@ public Cat(final ValueExpression left, final ValueExpression right) { @Override public Optional eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) { return ConcatenatedValueSource.create(ImmutableList.create(leftValue).add(rightValue)) - .flatMap(source -> createFromSource(source, ZERO, leftValue.getLength().add(rightValue.getLength()))) - .map(source -> new Value(source, encoding)); + .flatMap(source -> createFromSource(source, ZERO, leftValue.length().add(rightValue.length()))) + .map(source -> new CoreValue(source, encoding)); } } 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/ConstantFactory.java b/core/src/main/java/io/parsingdata/metal/expression/value/ConstantFactory.java index 0e895099..24d2976e 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/ConstantFactory.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/ConstantFactory.java @@ -29,7 +29,7 @@ public final class ConstantFactory { private ConstantFactory() {} public static Value createFromBytes(final byte[] value, final Encoding encoding) { - return new Value(Slice.createFromBytes(value), encoding); + return new CoreValue(Slice.createFromBytes(value), encoding); } public static Value createFromNumeric(final BigInteger value, final Encoding encoding) { @@ -41,14 +41,14 @@ public static Value createFromNumeric(final long value, final Encoding encoding) } public static Value createFromString(final String value, final Encoding encoding) { - return new Value(Slice.createFromBytes(value.getBytes(encoding.charset)), encoding); + return new CoreValue(Slice.createFromBytes(value.getBytes(encoding.charset)), encoding); } public static Value createFromBitSet(final BitSet value, final int minSize, final Encoding encoding) { final byte[] bytes = ByteOrder.LITTLE_ENDIAN.apply(value.toByteArray()); final byte[] outBytes = new byte[Math.max(minSize, bytes.length)]; System.arraycopy(bytes, 0, outBytes, outBytes.length - bytes.length, bytes.length); - return new Value(Slice.createFromBytes(outBytes), setToBigEndian(encoding)); + return new CoreValue(Slice.createFromBytes(outBytes), setToBigEndian(encoding)); } private static Encoding setToBigEndian(final Encoding encoding) { 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 new file mode 100644 index 00000000..b88bd243 --- /dev/null +++ b/core/src/main/java/io/parsingdata/metal/expression/value/CoreValue.java @@ -0,0 +1,97 @@ +/* + * 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 static io.parsingdata.metal.Util.bytesToHexString; +import static io.parsingdata.metal.Util.checkNotNull; + +import java.math.BigInteger; +import java.util.BitSet; +import java.util.Objects; + +import io.parsingdata.metal.Util; +import io.parsingdata.metal.data.Slice; +import io.parsingdata.metal.encoding.ByteOrder; +import io.parsingdata.metal.encoding.Encoding; +import io.parsingdata.metal.encoding.Sign; + +public class CoreValue implements Value { + + public static final BigInteger TO_STRING_BYTE_COUNT = BigInteger.valueOf(4); + + private final Slice slice; + private final Encoding encoding; + + public CoreValue(final Slice slice, final Encoding encoding) { + this.slice = checkNotNull(slice, "slice"); + this.encoding = checkNotNull(encoding, "encoding"); + } + + @Override + public Slice slice() { + return slice; + } + + @Override + public Encoding encoding() { + return encoding; + } + + @Override + public byte[] value() { + return slice.getData(); + } + + @Override + public BigInteger length() { + return slice.length; + } + + @Override + public BigInteger asNumeric() { + return encoding.sign == Sign.SIGNED ? new BigInteger(encoding.byteOrder.apply(value())) + : new BigInteger(1, encoding.byteOrder.apply(value())); + } + + @Override + public String asString() { + return new String(value(), encoding.charset); + } + + @Override + public BitSet asBitSet() { + return BitSet.valueOf(encoding.byteOrder == ByteOrder.BIG_ENDIAN ? ByteOrder.LITTLE_ENDIAN.apply(value()) : value()); + } + + @Override + public String toString() { + return "0x" + bytesToHexString(slice.getData(TO_STRING_BYTE_COUNT)) + (length().compareTo(TO_STRING_BYTE_COUNT) > 0 ? "..." : ""); + } + + @Override + public boolean equals(final Object obj) { + return Util.notNullAndSameClass(this, obj) + && Objects.equals(slice, ((CoreValue)obj).slice) + && Objects.equals(encoding, ((CoreValue)obj).encoding); + } + + @Override + public int hashCode() { + return Objects.hash(getClass(), slice, encoding); + } + +} diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Elvis.java b/core/src/main/java/io/parsingdata/metal/expression/value/Elvis.java index 7624d668..5f2e5351 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Elvis.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Elvis.java @@ -20,7 +20,7 @@ import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.data.Selection.reverse; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; import java.util.Optional; @@ -65,7 +65,7 @@ private Trampoline> eval(final ImmutableList result, if (rightValues.isEmpty()) { return complete(() -> result.add(reverse(leftValues))); } - return intermediate(() -> eval(result.add(leftValues.head != NOT_A_VALUE ? leftValues.head : rightValues.head), leftValues.tail, rightValues.tail)); + return intermediate(() -> eval(result.add(leftValues.head.equals(NOT_A_VALUE) ? rightValues.head : leftValues.head), leftValues.tail, rightValues.tail)); } @Override 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 50caa538..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 @@ -19,7 +19,7 @@ import static io.parsingdata.metal.Trampoline.complete; import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; @@ -33,19 +33,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 {@link 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 +57,10 @@ 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 == NOT_A_VALUE) { - throw new IllegalArgumentException("Count must evaluate to a single non-empty value."); - } - return expand(baseList, countList.head.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/Fold.java b/core/src/main/java/io/parsingdata/metal/expression/value/Fold.java index fee49abf..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 @@ -19,9 +19,10 @@ import static io.parsingdata.metal.Trampoline.complete; import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +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; @@ -31,50 +32,50 @@ 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}), * 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 * 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; - 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; } @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.head == NOT_A_VALUE) { - return ImmutableList.create(NOT_A_VALUE); + 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 initialValue; } final ImmutableList unpreparedValues = this.values.eval(parseState, encoding); if (unpreparedValues.isEmpty()) { - return initialList; + return initialValue; } if (containsNotAValue(unpreparedValues).computeResult()) { - return ImmutableList.create(NOT_A_VALUE); + return Optional.of(NOT_A_VALUE); } - final ImmutableList valueList = prepareValues(unpreparedValues).add(initialList); - return ImmutableList.create(fold(parseState, encoding, reducer, valueList.head, valueList.tail).computeResult()); + final ImmutableList valueList = initialValue.map(value -> prepareValues(unpreparedValues).add(value)).orElseGet(() -> prepareValues(unpreparedValues)); + 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) { - if (head == NOT_A_VALUE) { + if (head.equals(NOT_A_VALUE)) { return complete(() -> NOT_A_VALUE); } if (tail.isEmpty()) { @@ -91,7 +92,7 @@ private Trampoline containsNotAValue(final ImmutableList list) { if (list.isEmpty()) { return complete(() -> false); } - if (list.head != NOT_A_VALUE) { + if (!list.head.equals(NOT_A_VALUE)) { return intermediate(() -> containsNotAValue(list.tail)); } return complete(() -> true); 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 908d81da..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 Value(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 a8d54984..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). @@ -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..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). @@ -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/main/java/io/parsingdata/metal/expression/value/NotAValue.java b/core/src/main/java/io/parsingdata/metal/expression/value/NotAValue.java new file mode 100644 index 00000000..cf763b89 --- /dev/null +++ b/core/src/main/java/io/parsingdata/metal/expression/value/NotAValue.java @@ -0,0 +1,66 @@ +/* + * 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.math.BigInteger; +import java.util.BitSet; +import java.util.Objects; + +import io.parsingdata.metal.Util; +import io.parsingdata.metal.data.Slice; +import io.parsingdata.metal.encoding.Encoding; + +public final class NotAValue implements Value { + + public static final Value NOT_A_VALUE = new NotAValue(); + + private NotAValue() {} + + @Override public Slice slice() { throw unsupported(); } + + @Override public Encoding encoding() { throw unsupported(); } + + @Override public byte[] value() { throw unsupported(); } + + @Override public BigInteger length() { throw unsupported(); } + + @Override public BigInteger asNumeric() { throw unsupported(); } + + @Override public String asString() { throw unsupported(); } + + @Override public BitSet asBitSet() { throw unsupported(); } + + private UnsupportedOperationException unsupported() { + return new UnsupportedOperationException("NOT_A_VALUE does not support any Value operation."); + } + + @Override + public String toString() { + return "NOT_A_VALUE"; + } + + @Override + public boolean equals(final Object obj) { + return Util.notNullAndSameClass(this, obj); + } + + @Override + public int hashCode() { + return Objects.hash(getClass()); + } + +} 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..51f67681 --- /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 #evalSingle(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/UnaryValueExpression.java b/core/src/main/java/io/parsingdata/metal/expression/value/UnaryValueExpression.java index fa2aebc2..60aeb664 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/UnaryValueExpression.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/UnaryValueExpression.java @@ -20,7 +20,7 @@ import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.data.Selection.reverse; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; import java.util.Optional; @@ -69,7 +69,7 @@ private Trampoline> eval(final ImmutableList values, public abstract Optional eval(final Value value, final ParseState parseState, final Encoding encoding); private Value checkEval(final Value value, final ParseState parseState, final Encoding encoding) { - if (value == NOT_A_VALUE) { + if (value.equals(NOT_A_VALUE)) { return NOT_A_VALUE; } return eval(value, parseState, encoding).orElse(NOT_A_VALUE); diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/Value.java b/core/src/main/java/io/parsingdata/metal/expression/value/Value.java index 2ccc0338..674544d8 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/Value.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/Value.java @@ -16,69 +16,26 @@ package io.parsingdata.metal.expression.value; -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; -import java.util.Objects; -import io.parsingdata.metal.Util; import io.parsingdata.metal.data.Slice; -import io.parsingdata.metal.encoding.ByteOrder; import io.parsingdata.metal.encoding.Encoding; -import io.parsingdata.metal.encoding.Sign; - -public class Value { - - public static final Value NOT_A_VALUE = new Value(Slice.createFromBytes(new byte[]{}), DEFAULT_ENCODING); - public static final BigInteger TO_STRING_BYTE_COUNT = BigInteger.valueOf(4); - - public final Slice slice; - public final Encoding encoding; - - public Value(final Slice slice, final Encoding encoding) { - this.slice = checkNotNull(slice, "slice"); - this.encoding = checkNotNull(encoding, "encoding"); - } - public byte[] getValue() { - return slice.getData(); - } +public interface Value { - public BigInteger getLength() { - return slice.length; - } + Slice slice(); - public BigInteger asNumeric() { - return encoding.sign == Sign.SIGNED ? new BigInteger(encoding.byteOrder.apply(getValue())) - : new BigInteger(1, encoding.byteOrder.apply(getValue())); - } + Encoding encoding(); - public String asString() { - return new String(getValue(), encoding.charset); - } + byte[] value(); - public BitSet asBitSet() { - return BitSet.valueOf(encoding.byteOrder == ByteOrder.BIG_ENDIAN ? ByteOrder.LITTLE_ENDIAN.apply(getValue()) : getValue()); - } + BigInteger length(); - @Override - public String toString() { - return "0x" + bytesToHexString(slice.getData(TO_STRING_BYTE_COUNT)) + (getLength().compareTo(TO_STRING_BYTE_COUNT) > 0 ? "..." : ""); - } + BigInteger asNumeric(); - @Override - public boolean equals(final Object obj) { - return Util.notNullAndSameClass(this, obj) - && Objects.equals(slice, ((Value)obj).slice) - && Objects.equals(encoding, ((Value)obj).encoding); - } + String asString(); - @Override - public int hashCode() { - return Objects.hash(getClass(), slice, encoding); - } + BitSet asBitSet(); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/arithmetic/Div.java b/core/src/main/java/io/parsingdata/metal/expression/value/arithmetic/Div.java index 3de12323..ea7706d1 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/arithmetic/Div.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/arithmetic/Div.java @@ -18,7 +18,7 @@ import static java.math.BigInteger.ZERO; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Optional; diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/arithmetic/Mod.java b/core/src/main/java/io/parsingdata/metal/expression/value/arithmetic/Mod.java index e93ccf2c..077c8552 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/arithmetic/Mod.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/arithmetic/Mod.java @@ -18,7 +18,7 @@ import static java.math.BigInteger.ZERO; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Optional; diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/And.java b/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/And.java index 3186a593..3e921cae 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/And.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/And.java @@ -39,7 +39,7 @@ public And(final ValueExpression left, final ValueExpression right) { public Optional eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) { final BitSet leftBits = leftValue.asBitSet(); leftBits.and(rightValue.asBitSet()); - return Optional.of(ConstantFactory.createFromBitSet(leftBits, leftValue.getValue().length, encoding)); + return Optional.of(ConstantFactory.createFromBitSet(leftBits, leftValue.value().length, encoding)); } } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/Not.java b/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/Not.java index 4da8f734..38f07134 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/Not.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/Not.java @@ -38,8 +38,8 @@ public Not(final ValueExpression operand) { @Override public Optional eval(final Value value, final ParseState parseState, final Encoding encoding) { final BitSet bits = value.asBitSet(); - bits.flip(0, value.getValue().length * 8); - return Optional.of(ConstantFactory.createFromBitSet(bits, value.getValue().length, encoding)); + bits.flip(0, value.value().length * 8); + return Optional.of(ConstantFactory.createFromBitSet(bits, value.value().length, encoding)); } } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/Or.java b/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/Or.java index fa292b6a..589182d9 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/Or.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/Or.java @@ -39,7 +39,7 @@ public Or(final ValueExpression left, final ValueExpression right) { public Optional eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) { final BitSet leftBits = leftValue.asBitSet(); leftBits.or(rightValue.asBitSet()); - final int minSize = Math.max(leftValue.getValue().length, rightValue.getValue().length); + final int minSize = Math.max(leftValue.value().length, rightValue.value().length); return Optional.of(ConstantFactory.createFromBitSet(leftBits, minSize, encoding)); } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/ShiftRight.java b/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/ShiftRight.java index 0cfb8574..7f16c366 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/ShiftRight.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/bitwise/ShiftRight.java @@ -40,7 +40,7 @@ public ShiftRight(final ValueExpression operand, final ValueExpression positions public Optional eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) { final BitSet leftBits = leftValue.asBitSet(); final int shift = rightValue.asNumeric().intValueExact(); - return Optional.of(ConstantFactory.createFromBitSet(leftBits.get(shift, Math.max(shift, leftBits.length())), leftValue.getValue().length, encoding)); + return Optional.of(ConstantFactory.createFromBitSet(leftBits.get(shift, Math.max(shift, leftBits.length())), leftValue.value().length, encoding)); } } 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 273338d9..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 @@ -24,7 +24,7 @@ import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.encoding.Encoding.DEFAULT_ENCODING; import static io.parsingdata.metal.expression.value.ConstantFactory.createFromNumeric; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.math.BigInteger; import java.util.Objects; @@ -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(); @@ -73,7 +68,7 @@ private Optional getIteration(final ParseState parseState, final Encoding private BigInteger getLevel(final ParseState parseState, final Encoding encoding) { final ImmutableList levelValues = level.eval(parseState, encoding); - if (levelValues.size != 1 || levelValues.head == NOT_A_VALUE) { + if (levelValues.size != 1 || levelValues.head.equals(NOT_A_VALUE)) { throw new IllegalArgumentException("Level must evaluate to a single value."); } return levelValues.head.asNumeric(); 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/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/expression/value/reference/Len.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Len.java index d76e8f1f..b39045f7 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Len.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Len.java @@ -39,7 +39,7 @@ public Len(final ValueExpression operand) { @Override public Optional eval(final Value value, final ParseState parseState, final Encoding encoding) { - return Optional.of(fromNumeric(value.getLength())); + return Optional.of(fromNumeric(value.length())); } private static Value fromNumeric(final BigInteger length) { 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 ef7257a3..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 @@ -23,17 +23,17 @@ import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.data.Selection.reverse; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +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.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.NotAValue; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; @@ -44,9 +44,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 ({@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 { @@ -69,7 +69,7 @@ private Trampoline> eval(final ImmutableList values, } final BigInteger valueCount = BigInteger.valueOf(values.size); final Value index = indices.head; - final Value nextResult = index != NOT_A_VALUE && index.asNumeric().compareTo(valueCount) < 0 && index.asNumeric().compareTo(ZERO) >= 0 + final Value nextResult = !index.equals(NOT_A_VALUE) && index.asNumeric().compareTo(valueCount) < 0 && index.asNumeric().compareTo(ZERO) >= 0 ? nth(values, valueCount.subtract(index.asNumeric()).subtract(ONE)).computeResult() : NOT_A_VALUE; return intermediate(() -> eval(values, indices.tail, result.add(nextResult))); diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Offset.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Offset.java index cc7646c2..67c7aae5 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Offset.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Offset.java @@ -40,7 +40,7 @@ public class Offset extends UnaryValueExpression { @Override public Optional eval(final Value value, final ParseState parseState, final Encoding encoding) { - return Optional.of(ConstantFactory.createFromNumeric(value.slice.offset, value.encoding)); + return Optional.of(ConstantFactory.createFromNumeric(value.slice().offset, value.encoding())); } } diff --git a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Ref.java b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Ref.java index 883f87c1..c2ce73e1 100644 --- a/core/src/main/java/io/parsingdata/metal/expression/value/reference/Ref.java +++ b/core/src/main/java/io/parsingdata/metal/expression/value/reference/Ref.java @@ -21,7 +21,7 @@ import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.data.Selection.NO_LIMIT; import static io.parsingdata.metal.data.Selection.getAllValues; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; import java.util.function.Predicate; @@ -76,7 +76,7 @@ public ImmutableList eval(final ParseState parseState, final Encoding enc if (evaluatedLimit.size != 1) { throw new IllegalArgumentException("Limit must evaluate to a single non-empty value."); } - if (evaluatedLimit.head == NOT_A_VALUE) { + if (evaluatedLimit.head.equals(NOT_A_VALUE)) { return ImmutableList.create(NOT_A_VALUE); } return evalImpl(parseState, evaluatedLimit.head.asNumeric().intValueExact()); 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 756e9dc4..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,23 +16,25 @@ package io.parsingdata.metal.expression.value.reference; +import static java.util.function.Function.identity; + +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 io.parsingdata.metal.expression.value.Value} most recently added to - * the parse state. + * 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().map(identity()); } @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 1661f290..72f3b922 100644 --- a/core/src/main/java/io/parsingdata/metal/token/Def.java +++ b/core/src/main/java/io/parsingdata/metal/token/Def.java @@ -20,57 +20,50 @@ 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.Value.NOT_A_VALUE; +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.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 == 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(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) { 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/main/java/io/parsingdata/metal/token/RepN.java b/core/src/main/java/io/parsingdata/metal/token/RepN.java index 13f64d30..482bfd63 100644 --- a/core/src/main/java/io/parsingdata/metal/token/RepN.java +++ b/core/src/main/java/io/parsingdata/metal/token/RepN.java @@ -18,48 +18,42 @@ import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.Util.failure; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +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 == 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 diff --git a/core/src/main/java/io/parsingdata/metal/token/Sub.java b/core/src/main/java/io/parsingdata/metal/token/Sub.java index 326cb327..f043d83e 100644 --- a/core/src/main/java/io/parsingdata/metal/token/Sub.java +++ b/core/src/main/java/io/parsingdata/metal/token/Sub.java @@ -21,7 +21,7 @@ import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.Util.success; import static io.parsingdata.metal.data.Selection.hasRootAtOffset; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.math.BigInteger; import java.util.Objects; @@ -72,7 +72,7 @@ private Trampoline> iterate(final Environment environment, if (offsetList.isEmpty()) { return complete(() -> success(environment.parseState.closeBranch(this))); } - if (offsetList.head == NOT_A_VALUE) { + if (offsetList.head.equals(NOT_A_VALUE)) { return complete(Util::failure); } return parse(environment, offsetList.head.asNumeric()) diff --git a/core/src/main/java/io/parsingdata/metal/token/Tie.java b/core/src/main/java/io/parsingdata/metal/token/Tie.java index f54b6959..1716ee03 100644 --- a/core/src/main/java/io/parsingdata/metal/token/Tie.java +++ b/core/src/main/java/io/parsingdata/metal/token/Tie.java @@ -20,6 +20,7 @@ import static io.parsingdata.metal.Trampoline.intermediate; import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.Util.success; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.util.Objects; import java.util.Optional; @@ -68,7 +69,7 @@ private Trampoline> iterate(final Environment environment, if (values.isEmpty()) { return complete(() -> success(new ParseState(environment.parseState.closeBranch(this).order, returnParseState.source, returnParseState.offset, returnParseState.iterations))); } - if (values.head == Value.NOT_A_VALUE) { + if (values.head.equals(NOT_A_VALUE)) { return complete(Util::failure); } return token.parse(environment.withParseState(environment.parseState.withSource(new DataExpressionSource(dataExpression, index, environment.parseState, environment.encoding)))) diff --git a/core/src/main/java/io/parsingdata/metal/token/Until.java b/core/src/main/java/io/parsingdata/metal/token/Until.java index 24552aaa..2ad401c3 100644 --- a/core/src/main/java/io/parsingdata/metal/token/Until.java +++ b/core/src/main/java/io/parsingdata/metal/token/Until.java @@ -24,7 +24,7 @@ import static io.parsingdata.metal.Util.checkNotEmpty; import static io.parsingdata.metal.Util.checkNotNull; import static io.parsingdata.metal.Util.success; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +import static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; import java.math.BigInteger; import java.util.Objects; @@ -121,7 +121,7 @@ private Trampoline> parseSlice(final Environment environmen } private boolean checkNotValidList(final ImmutableList list) { - return list.isEmpty() || list.head == NOT_A_VALUE; + return list.isEmpty() || list.head.equals(NOT_A_VALUE); } private BigInteger getNumeric(final ImmutableList list) { diff --git a/core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java b/core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java index 78e60f4c..dc094fe0 100644 --- a/core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java +++ b/core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java @@ -26,11 +26,13 @@ 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; import static io.parsingdata.metal.data.ParseState.createFromByteStream; import static io.parsingdata.metal.data.Slice.createFromBytes; +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.TokenDefinitions.any; @@ -79,12 +81,14 @@ import io.parsingdata.metal.expression.value.Cat; import io.parsingdata.metal.expression.value.Const; import io.parsingdata.metal.expression.value.ConstantFactory; +import io.parsingdata.metal.expression.value.CoreValue; import io.parsingdata.metal.expression.value.Elvis; import io.parsingdata.metal.expression.value.Expand; import io.parsingdata.metal.expression.value.FoldCat; import io.parsingdata.metal.expression.value.FoldLeft; import io.parsingdata.metal.expression.value.FoldRight; import io.parsingdata.metal.expression.value.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; @@ -145,9 +149,10 @@ 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())); + 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); private static final List> SLICES = Arrays.asList(() -> createFromBytes(new byte[] { 1, 2 }), () -> Slice.createFromSource(new DataExpressionSource(ref("a"), 1, createFromByteStream(DUMMY_STREAM).add(PARSE_VALUE).add(PARSE_VALUE), enc()), ZERO, BigInteger.valueOf(2)).get()); private static final List> BYTE_ARRAYS = Arrays.asList(() -> new byte[] { 0 }, () -> new byte[] { 1, 2 }, () -> new byte[] {}); @@ -168,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); @@ -203,7 +209,7 @@ public static Collection data() throws IllegalAccessException, Invocat io.parsingdata.metal.expression.logical.And.class, io.parsingdata.metal.expression.logical.Or.class, io.parsingdata.metal.expression.logical.Not.class, // Data structures - Value.class, ParseValue.class, ParseReference.class, ParseState.class, + CoreValue.class, ParseValue.class, ParseReference.class, ParseState.class, // Inputs ConstantSource.class, DataExpressionSource.class, ByteStreamSource.class, ConcatenatedValueSource.class ); 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..62ca273a 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 @@ -62,7 +62,7 @@ public void testValidLength() { assertTrue(result.isPresent()); assertArrayEquals( new byte[]{0x04, 0x08}, - getValue(result.get().order, "data").getValue() + getValue(result.get().order, "data").value() ); } @@ -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..d73c43b5 100644 --- a/core/src/test/java/io/parsingdata/metal/ErrorsTest.java +++ b/core/src/test/java/io/parsingdata/metal/ErrorsTest.java @@ -23,25 +23,20 @@ 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; -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 { @@ -53,37 +48,25 @@ 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()); } - @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/ShorthandsTest.java b/core/src/test/java/io/parsingdata/metal/ShorthandsTest.java index c6bc2844..36666ea5 100644 --- a/core/src/test/java/io/parsingdata/metal/ShorthandsTest.java +++ b/core/src/test/java/io/parsingdata/metal/ShorthandsTest.java @@ -230,7 +230,7 @@ public void whenTrue() { Optional result = when(def("name", con(1), eq(con(1))), TRUE).parse(env(stream(1))); assertTrue(result.isPresent()); assertEquals(1, result.get().offset.intValueExact()); - assertEquals(1, Selection.getAllValues(result.get().order, parseValue -> parseValue.matches("name") && parseValue.getValue().length == 1 && parseValue.getValue()[0] == 1).size); + assertEquals(1, Selection.getAllValues(result.get().order, parseValue -> parseValue.matches("name") && parseValue.value().length == 1 && parseValue.value()[0] == 1).size); } @Test @@ -241,7 +241,7 @@ public void whenFalse() { def("name2", con(1), eq(con(2)))).parse(env(stream(2))); assertTrue(result.isPresent()); assertEquals(1, result.get().offset.intValueExact()); - assertEquals(1, Selection.getAllValues(result.get().order, parseValue -> parseValue.matches("name2") && parseValue.getValue().length == 1 && parseValue.getValue()[0] == 2).size); + assertEquals(1, Selection.getAllValues(result.get().order, parseValue -> parseValue.matches("name2") && parseValue.value().length == 1 && parseValue.value()[0] == 2).size); } } diff --git a/core/src/test/java/io/parsingdata/metal/SubStructTableTest.java b/core/src/test/java/io/parsingdata/metal/SubStructTableTest.java index af9a25d3..bab14a6c 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 @@ -94,7 +95,7 @@ private void checkStruct(final ParseGraph graph, final int offsetHeader) { assertTrue(graph.head.isGraph()); assertEquals(84, graph.head.asGraph().head.asValue().asNumeric().intValueExact()); assertEquals(42, graph.tail.head.asGraph().head.asValue().asNumeric().intValueExact()); - assertEquals(offsetHeader, graph.tail.head.asGraph().head.asValue().slice.offset.intValueExact()); + assertEquals(offsetHeader, graph.tail.head.asGraph().head.asValue().slice().offset.intValueExact()); } } diff --git a/core/src/test/java/io/parsingdata/metal/SubStructTest.java b/core/src/test/java/io/parsingdata/metal/SubStructTest.java index f90e6ff2..a21eaf74 100644 --- a/core/src/test/java/io/parsingdata/metal/SubStructTest.java +++ b/core/src/test/java/io/parsingdata/metal/SubStructTest.java @@ -148,7 +148,7 @@ private void checkLeaf(final ParseGraph graph, final int graphOffset, final int private void checkValue(final ParseItem item, final int value, final int offset) { assertTrue(item.isValue()); assertEquals(value, item.asValue().asNumeric().intValueExact()); - assertEquals(offset, item.asValue().slice.offset.intValueExact()); + assertEquals(offset, item.asValue().slice().offset.intValueExact()); } @Test diff --git a/core/src/test/java/io/parsingdata/metal/ToStringTest.java b/core/src/test/java/io/parsingdata/metal/ToStringTest.java index 4469e928..76853926 100644 --- a/core/src/test/java/io/parsingdata/metal/ToStringTest.java +++ b/core/src/test/java/io/parsingdata/metal/ToStringTest.java @@ -68,6 +68,7 @@ import static io.parsingdata.metal.Shorthand.whl; import static io.parsingdata.metal.data.ParseGraph.NONE; import static io.parsingdata.metal.data.Slice.createFromBytes; +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.ParseStateFactory.stream; import static io.parsingdata.metal.util.TokenDefinitions.any; @@ -88,6 +89,7 @@ import io.parsingdata.metal.encoding.Encoding; import io.parsingdata.metal.encoding.Sign; import io.parsingdata.metal.expression.Expression; +import io.parsingdata.metal.expression.value.CoreValue; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; import io.parsingdata.metal.token.Token; @@ -105,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()), last(v())), e)); final String output = t.toString(); for (int i = 0; i < count; i++) { assertTrue(output.contains(prefix + i)); @@ -122,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(1)), 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 @@ -151,6 +153,8 @@ public void specialExpressions() { assertEquals("CurrentOffset", CURRENT_OFFSET.toString()); assertTrue(v().toString().contains("CurrentIteration")); assertEquals("CurrentIteration", CURRENT_ITERATION.toString()); + assertTrue(v().toString().contains("NOT_A_VALUE")); + assertEquals("NOT_A_VALUE", NOT_A_VALUE.toString()); } @Test @@ -174,7 +178,7 @@ public void data() { final ParseValue pv1 = new ParseValue("name", NONE, createFromBytes(new byte[]{1, 2}), enc()); final String pv1String = "pval(name:0x0102)"; final Optional ov1 = Optional.of(pv1); - final Optional ov2 = Optional.of(new Value(createFromBytes(new byte[]{3}), enc())); + final Optional ov2 = Optional.of(new CoreValue(createFromBytes(new byte[]{3}), enc())); assertEquals(">Optional[0x03]>Optional[" + pv1String + "]", ImmutableList.create(ov1).add(ov2).toString()); final ParseValue pv2 = new ParseValue("two", NONE, createFromBytes(new byte[]{3, 4}), enc()); final String pv2String = "pval(two:0x0304)"; diff --git a/core/src/test/java/io/parsingdata/metal/TreeTest.java b/core/src/test/java/io/parsingdata/metal/TreeTest.java index 67243436..d21522ce 100644 --- a/core/src/test/java/io/parsingdata/metal/TreeTest.java +++ b/core/src/test/java/io/parsingdata/metal/TreeTest.java @@ -106,7 +106,7 @@ private void checkHeader(final ParseGraph header, final int offset) { assertTrue(head.isValue()); assertTrue(head.asValue().matches("head")); assertEquals(HEAD, head.asValue().asNumeric().intValueExact()); - assertEquals(offset, head.asValue().slice.offset.intValueExact()); + assertEquals(offset, head.asValue().slice().offset.intValueExact()); final ParseItem nr = header.head; // head = Def("nr") assertTrue(nr.isValue()); assertTrue(nr.asValue().matches("nr")); diff --git a/core/src/test/java/io/parsingdata/metal/UtilInflateTest.java b/core/src/test/java/io/parsingdata/metal/UtilInflateTest.java index e2a9f80f..09b0d35b 100644 --- a/core/src/test/java/io/parsingdata/metal/UtilInflateTest.java +++ b/core/src/test/java/io/parsingdata/metal/UtilInflateTest.java @@ -20,7 +20,7 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Util.inflate; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +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.ParseStateFactory.stream; diff --git a/core/src/test/java/io/parsingdata/metal/data/ConcatenatedValueSourceTest.java b/core/src/test/java/io/parsingdata/metal/data/ConcatenatedValueSourceTest.java index 2d15cd1c..ec81a384 100644 --- a/core/src/test/java/io/parsingdata/metal/data/ConcatenatedValueSourceTest.java +++ b/core/src/test/java/io/parsingdata/metal/data/ConcatenatedValueSourceTest.java @@ -31,6 +31,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; +import io.parsingdata.metal.expression.value.CoreValue; import io.parsingdata.metal.expression.value.Value; @RunWith(Parameterized.class) @@ -46,8 +47,8 @@ private static ImmutableList createValues() { final byte[] twoSliceSource = new byte[] { -1, -1, 5, 6, 7, 8, 9, -1, -1, 10, 11, 12, 13, 14, -1, -1 }; return ImmutableList .create(createFromBytes(new byte[] { 0, 1, 2, 3, 4 }, enc())) - .add(new Value(createFromSource(new ConstantSource(twoSliceSource), BigInteger.valueOf(2), BigInteger.valueOf(5)).get(), enc())) - .add(new Value(createFromSource(new ConstantSource(twoSliceSource), BigInteger.valueOf(9), BigInteger.valueOf(5)).get(), enc())) + .add(new CoreValue(createFromSource(new ConstantSource(twoSliceSource), BigInteger.valueOf(2), BigInteger.valueOf(5)).get(), enc())) + .add(new CoreValue(createFromSource(new ConstantSource(twoSliceSource), BigInteger.valueOf(9), BigInteger.valueOf(5)).get(), enc())) .add(createFromBytes(new byte[] { 15, 16, 17, 18, 19 }, enc())) .add(createFromBytes(new byte[] { 20, 21, 22, 23, 24 }, enc())); } diff --git a/core/src/test/java/io/parsingdata/metal/data/DataExpressionSourceTest.java b/core/src/test/java/io/parsingdata/metal/data/DataExpressionSourceTest.java index 0a05c565..b7a43573 100644 --- a/core/src/test/java/io/parsingdata/metal/data/DataExpressionSourceTest.java +++ b/core/src/test/java/io/parsingdata/metal/data/DataExpressionSourceTest.java @@ -63,8 +63,8 @@ private Optional setupResult() { @Test public void createSliceFromParseValue() { final ParseValue value = setupValue(); - assertTrue(value.slice.source.isAvailable(ZERO, BigInteger.valueOf(4))); - assertFalse(value.slice.source.isAvailable(ZERO, BigInteger.valueOf(5))); + assertTrue(value.slice().source.isAvailable(ZERO, BigInteger.valueOf(4))); + assertFalse(value.slice().source.isAvailable(ZERO, BigInteger.valueOf(5))); } @Test diff --git a/core/src/test/java/io/parsingdata/metal/data/ParseGraphTest.java b/core/src/test/java/io/parsingdata/metal/data/ParseGraphTest.java index c99cdbab..aa298eb0 100644 --- a/core/src/test/java/io/parsingdata/metal/data/ParseGraphTest.java +++ b/core/src/test/java/io/parsingdata/metal/data/ParseGraphTest.java @@ -132,7 +132,7 @@ private ParseGraph makeCycleGraph() { .add(a) .addBranch(t) .add(b) - .add(new ParseReference(a.slice.offset, a.slice.source, aDef)) + .add(new ParseReference(a.slice().offset, a.slice().source, aDef)) .closeBranch(); } diff --git a/core/src/test/java/io/parsingdata/metal/data/ParseValueTest.java b/core/src/test/java/io/parsingdata/metal/data/ParseValueTest.java index 40fafd8e..44e7cd93 100644 --- a/core/src/test/java/io/parsingdata/metal/data/ParseValueTest.java +++ b/core/src/test/java/io/parsingdata/metal/data/ParseValueTest.java @@ -54,8 +54,8 @@ public void setUp() { public void state() { assertThat(value.name, is("value")); assertThat(value.getDefinition(), is(definition)); - assertThat(value.slice.offset.longValueExact(), is(0L)); - assertThat(value.getValue(), is(equalTo(new byte[] { 1 }))); + assertThat(value.slice().offset.longValueExact(), is(0L)); + assertThat(value.value(), is(equalTo(new byte[] { 1 }))); } @Test 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 ccb1a7a0..71f852e3 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)); @@ -98,10 +98,10 @@ public void retrievePartialDataFromSlice() { @Test public void sliceToString() { final ParseValue pv1 = new ParseValue("name", NONE, createFromBytes(new byte[]{1, 2}), enc()); - assertEquals("Slice(ConstantSource(0x0102)@0:2)", pv1.slice.toString()); + assertEquals("Slice(ConstantSource(0x0102)@0:2)", pv1.slice().toString()); final ParseState oneValueParseState = stream().add(pv1); final ParseState twoValueParseState = oneValueParseState.add(new ParseValue("name2", NONE, Slice.createFromSource(new DataExpressionSource(ref("name"), 0, oneValueParseState, enc()), ZERO, BigInteger.valueOf(2)).get(), enc())); - final String dataExpressionSliceString = getValue(twoValueParseState.order, "name2").slice.toString(); + final String dataExpressionSliceString = getValue(twoValueParseState.order, "name2").slice().toString(); assertTrue(dataExpressionSliceString.startsWith("Slice(DataExpressionSource(NameRef(name)[0](")); assertTrue(dataExpressionSliceString.endsWith(")@0:2)")); } diff --git a/core/src/test/java/io/parsingdata/metal/data/SourceAndSliceTest.java b/core/src/test/java/io/parsingdata/metal/data/SourceAndSliceTest.java index 5571673f..40aa82b0 100644 --- a/core/src/test/java/io/parsingdata/metal/data/SourceAndSliceTest.java +++ b/core/src/test/java/io/parsingdata/metal/data/SourceAndSliceTest.java @@ -39,6 +39,7 @@ import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; +import io.parsingdata.metal.expression.value.CoreValue; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.util.InMemoryByteStream; @@ -57,7 +58,7 @@ public static Collection data() { { new ConstantSource(DATA) }, { new DataExpressionSource(con(DATA), 0, EMPTY_PARSE_STATE, enc()) }, { new ByteStreamSource(new InMemoryByteStream(DATA)) }, - { ConcatenatedValueSource.create(ImmutableList.create(new Value(createFromSource(new ConstantSource(DATA), ZERO, BigInteger.valueOf(2)).get(), enc())).add(new Value(createFromSource(new ConstantSource(DATA), BigInteger.valueOf(2), BigInteger.valueOf(2)).get(), enc()))).get() } + { ConcatenatedValueSource.create(ImmutableList.create(new CoreValue(createFromSource(new ConstantSource(DATA), ZERO, BigInteger.valueOf(2)).get(), enc())).add(new CoreValue(createFromSource(new ConstantSource(DATA), BigInteger.valueOf(2), BigInteger.valueOf(2)).get(), enc()))).get() } }); } diff --git a/core/src/test/java/io/parsingdata/metal/data/callback/CallbackTest.java b/core/src/test/java/io/parsingdata/metal/data/callback/CallbackTest.java index 190c543b..5dbd71b6 100644 --- a/core/src/test/java/io/parsingdata/metal/data/callback/CallbackTest.java +++ b/core/src/test/java/io/parsingdata/metal/data/callback/CallbackTest.java @@ -93,7 +93,7 @@ private Callbacks createCallbackList(Token token, final long... offsets) { @Override public void handleSuccess(Token token, ParseState before, ParseState after) { final ImmutableList roots = getAllRoots(after.order, token); - assertEquals(offsets[count++], roots.head.asGraph().tail.head.asValue().slice.offset.longValueExact()); + assertEquals(offsets[count++], roots.head.asGraph().tail.head.asValue().slice().offset.longValueExact()); } @Override @@ -130,8 +130,8 @@ public void handleSuccess(Token token, ParseState before, ParseState after) { assertEquals(2, seqRoots.size); // verify order of the two Seq graphs: - assertEquals(2, getValue(seqRoots.head.asGraph(), "a").slice.offset.intValueExact()); - assertEquals(0, getValue(seqRoots.tail.head.asGraph(), "a").slice.offset.intValueExact()); + assertEquals(2, getValue(seqRoots.head.asGraph(), "a").slice().offset.intValueExact()); + assertEquals(0, getValue(seqRoots.tail.head.asGraph(), "a").slice().offset.intValueExact()); } @Override diff --git a/core/src/test/java/io/parsingdata/metal/data/selection/ByTokenTest.java b/core/src/test/java/io/parsingdata/metal/data/selection/ByTokenTest.java index 0e7e6819..be61913b 100644 --- a/core/src/test/java/io/parsingdata/metal/data/selection/ByTokenTest.java +++ b/core/src/test/java/io/parsingdata/metal/data/selection/ByTokenTest.java @@ -192,7 +192,7 @@ public void getAllSub() { assertThat(items.size, is(equalTo(2L))); assertThat(items.head.getDefinition(), is(equalTo(TWO_BYTES))); - assertThat(items.head.asValue().getValue(), is(equalTo(new byte[]{2, 3}))); + assertThat(items.head.asValue().value(), is(equalTo(new byte[]{2, 3}))); } @Test @@ -255,7 +255,7 @@ public void getAllRootsSingle() { assertEquals(SMALL_SEQ, seqItems.head.getDefinition()); final ParseValue c = seqItems.head.asGraph().head.asValue(); assertEquals(3, c.asNumeric().intValueExact()); - assertEquals(2, c.slice.offset.intValueExact()); + assertEquals(2, c.slice().offset.intValueExact()); } @Test 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 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 ac9fae8a..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 @@ -34,7 +34,7 @@ import static io.parsingdata.metal.data.Slice.createFromBytes; import static io.parsingdata.metal.encoding.Encoding.DEFAULT_ENCODING; import static io.parsingdata.metal.expression.value.BytesTest.EMPTY_PARSE_STATE; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +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.EnvironmentFactory.env; import static io.parsingdata.metal.util.ParseStateFactory.stream; @@ -80,30 +80,7 @@ public void foldToEmpty() { @Test public void inputContainsEmptyInTail() { - assertEquals(NOT_A_VALUE, foldRight((parseState, encoding) -> ImmutableList.create(NOT_A_VALUE).add(new Value(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); + 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 diff --git a/core/src/test/java/io/parsingdata/metal/expression/value/NotAValueTest.java b/core/src/test/java/io/parsingdata/metal/expression/value/NotAValueTest.java new file mode 100644 index 00000000..acc7a92e --- /dev/null +++ b/core/src/test/java/io/parsingdata/metal/expression/value/NotAValueTest.java @@ -0,0 +1,79 @@ +/* + * 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 static io.parsingdata.metal.expression.value.NotAValue.NOT_A_VALUE; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class NotAValueTest { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + @Test + public void getSlice() { + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("NOT_A_VALUE does not support any Value operation."); + NOT_A_VALUE.slice(); + } + + @Test + public void getEncoding() { + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("NOT_A_VALUE does not support any Value operation."); + NOT_A_VALUE.encoding(); + } + + @Test + public void getValue() { + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("NOT_A_VALUE does not support any Value operation."); + NOT_A_VALUE.value(); + } + + @Test + public void getLength() { + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("NOT_A_VALUE does not support any Value operation."); + NOT_A_VALUE.length(); + } + + @Test + public void asNumeric() { + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("NOT_A_VALUE does not support any Value operation."); + NOT_A_VALUE.asNumeric(); + } + + @Test + public void asString() { + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("NOT_A_VALUE does not support any Value operation."); + NOT_A_VALUE.asString(); + } + + @Test + public void asBitSet() { + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("NOT_A_VALUE does not support any Value operation."); + NOT_A_VALUE.asBitSet(); + } + +} 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 6af5daad..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,11 +21,12 @@ 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; import static io.parsingdata.metal.Shorthand.seq; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +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; @@ -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")); diff --git a/core/src/test/java/io/parsingdata/metal/expression/value/RefEdgeCaseTest.java b/core/src/test/java/io/parsingdata/metal/expression/value/RefEdgeCaseTest.java index 1a8b191c..67655347 100644 --- a/core/src/test/java/io/parsingdata/metal/expression/value/RefEdgeCaseTest.java +++ b/core/src/test/java/io/parsingdata/metal/expression/value/RefEdgeCaseTest.java @@ -23,7 +23,7 @@ import static io.parsingdata.metal.Shorthand.exp; import static io.parsingdata.metal.Shorthand.ref; import static io.parsingdata.metal.Shorthand.rep; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +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.EnvironmentFactory.env; import static io.parsingdata.metal.util.ParseStateFactory.stream; diff --git a/core/src/test/java/io/parsingdata/metal/expression/value/ValueExpressionEvalEmptyTest.java b/core/src/test/java/io/parsingdata/metal/expression/value/ValueExpressionEvalEmptyTest.java index 68356aae..c63aa7e4 100644 --- a/core/src/test/java/io/parsingdata/metal/expression/value/ValueExpressionEvalEmptyTest.java +++ b/core/src/test/java/io/parsingdata/metal/expression/value/ValueExpressionEvalEmptyTest.java @@ -21,7 +21,7 @@ import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.div; import static io.parsingdata.metal.Shorthand.mod; -import static io.parsingdata.metal.expression.value.Value.NOT_A_VALUE; +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.ParseStateFactory.stream; 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/token/TieTest.java b/core/src/test/java/io/parsingdata/metal/token/TieTest.java index d0cba266..e9366c94 100644 --- a/core/src/test/java/io/parsingdata/metal/token/TieTest.java +++ b/core/src/test/java/io/parsingdata/metal/token/TieTest.java @@ -90,7 +90,7 @@ private Optional parseContainer() { @Test public void checkContainerSource() { final Optional result = parseContainer(); - checkFullParse(INC_PREV_MOD_100, fold(ref("value"), Shorthand::cat).eval(result.get(), enc()).head.getValue()); + checkFullParse(INC_PREV_MOD_100, fold(ref("value"), Shorthand::cat).eval(result.get(), enc()).head.value()); } private Optional checkFullParse(Token token, byte[] data) { 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/Callback.java b/formats/src/main/java/io/parsingdata/metal/format/Callback.java index 00924789..01369ddc 100644 --- a/formats/src/main/java/io/parsingdata/metal/format/Callback.java +++ b/formats/src/main/java/io/parsingdata/metal/format/Callback.java @@ -23,6 +23,7 @@ import io.parsingdata.metal.data.ParseState; import io.parsingdata.metal.encoding.Encoding; +import io.parsingdata.metal.expression.value.CoreValue; import io.parsingdata.metal.expression.value.UnaryValueExpression; import io.parsingdata.metal.expression.value.Value; import io.parsingdata.metal.expression.value.ValueExpression; @@ -36,9 +37,9 @@ public static ValueExpression crc32(final ValueExpression target) { @Override public Optional eval(final Value value, final ParseState parseState, final Encoding encoding) { final CRC32 crc = new CRC32(); - crc.update(value.getValue()); + crc.update(value.value()); final long crcValue = crc.getValue(); - return Optional.of(new Value(createFromBytes(encoding.byteOrder.apply(new byte[] { + return Optional.of(new CoreValue(createFromBytes(encoding.byteOrder.apply(new byte[] { (byte)((crcValue & 0xff000000) >> 24), (byte)((crcValue & 0xff0000) >> 16), (byte)((crcValue & 0xff00) >> 8), 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/CallbackTest.java b/formats/src/test/java/io/parsingdata/metal/format/CallbackTest.java index dda1bb26..f5dbf99f 100644 --- a/formats/src/test/java/io/parsingdata/metal/format/CallbackTest.java +++ b/formats/src/test/java/io/parsingdata/metal/format/CallbackTest.java @@ -36,7 +36,7 @@ public class CallbackTest { public void crc32Good() { final ImmutableList result = crc32(con(0x01020304)).eval(stream(), enc()); assertEquals(1, result.size); - assertArrayEquals(new byte[] { -74, 60, -5, -51 }, result.head.getValue()); + assertArrayEquals(new byte[] { -74, 60, -5, -51 }, result.head.value()); } @Test 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})")