Skip to content

Commit

Permalink
#70: Added SingleValueExpression as a specialization of ValueExpressi…
Browse files Browse the repository at this point in the history
…on that returns a single (optional) Value. Applied it to a small set of cases.
  • Loading branch information
jvdb committed Jan 26, 2019
1 parent a00d38c commit 94a8d2a
Show file tree
Hide file tree
Showing 16 changed files with 85 additions and 55 deletions.
35 changes: 18 additions & 17 deletions core/src/main/java/io/parsingdata/metal/Shorthand.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import io.parsingdata.metal.expression.value.FoldLeft;
import io.parsingdata.metal.expression.value.FoldRight;
import io.parsingdata.metal.expression.value.Reverse;
import io.parsingdata.metal.expression.value.SingleValueExpression;
import io.parsingdata.metal.expression.value.UnaryValueExpression;
import io.parsingdata.metal.expression.value.Value;
import io.parsingdata.metal.expression.value.ValueExpression;
Expand Down Expand Up @@ -96,15 +97,15 @@ public final class Shorthand {

private Shorthand() {}

public static Token def(final String name, final ValueExpression size, final Expression predicate, final Encoding encoding) { return post(def(name, size, encoding), predicate); }
public static Token def(final String name, final ValueExpression size, final Expression predicate) { return def(name, size, predicate, null); }
public static Token def(final String name, final ValueExpression size, final Encoding encoding) { return new Def(name, size, encoding); }
public static Token def(final String name, final ValueExpression size) { return def(name, size, (Encoding)null); }
public static Token def(final String name, final SingleValueExpression size, final Expression predicate, final Encoding encoding) { return post(def(name, size, encoding), predicate); }
public static Token def(final String name, final SingleValueExpression size, final Expression predicate) { return def(name, size, predicate, null); }
public static Token def(final String name, final SingleValueExpression size, final Encoding encoding) { return new Def(name, size, encoding); }
public static Token def(final String name, final SingleValueExpression size) { return def(name, size, (Encoding)null); }
public static Token def(final String name, final long size, final Expression predicate, final Encoding encoding) { return def(name, con(size), predicate, encoding); }
public static Token def(final String name, final long size, final Expression predicate) { return def(name, size, predicate, null); }
public static Token def(final String name, final long size, final Encoding encoding) { return def(name, con(size), encoding); }
public static Token def(final String name, final long size) { return def(name, size, (Encoding)null); }
public static Token nod(final ValueExpression size) { return def(EMPTY_NAME, size); }
public static Token nod(final SingleValueExpression size) { return def(EMPTY_NAME, size); }
public static Token nod(final long size) { return nod(con(size)); }
public static Token cho(final String name, final Encoding encoding, final Token token1, final Token token2, final Token... tokens) { return new Cho(name, encoding, token1, token2, tokens); }
public static Token cho(final String name, final Token token1, final Token token2, final Token... tokens) { return cho(name, null, token1, token2, tokens); }
Expand Down Expand Up @@ -171,24 +172,24 @@ private Shorthand() {}
public static UnaryValueExpression not(final ValueExpression operand) { return new io.parsingdata.metal.expression.value.bitwise.Not(operand); }
public static BinaryValueExpression shl(final ValueExpression left, final ValueExpression right) { return new ShiftLeft(left, right); }
public static BinaryValueExpression shr(final ValueExpression left, final ValueExpression right) { return new ShiftRight(left, right); }
public static ValueExpression con(final long value) { return con(value, DEFAULT_ENCODING); }
public static ValueExpression con(final long value, final Encoding encoding) { return con(ConstantFactory.createFromNumeric(value, encoding)); }
public static ValueExpression con(final String value) { return con(value, DEFAULT_ENCODING); }
public static ValueExpression con(final String value, final Encoding encoding) { return con(ConstantFactory.createFromString(value, encoding)); }
public static ValueExpression con(final Value value) { return new Const(value); }
public static ValueExpression con(final Encoding encoding, final int... values) { return new Const(new 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 Value(createFromBytes(toByteArray(values)), encoding)); }
public static SingleValueExpression con(final int... values) { return con(DEFAULT_ENCODING, values); }
public static SingleValueExpression con(final byte[] value) { return con(value, DEFAULT_ENCODING); }
public static SingleValueExpression con(final byte[] value, final Encoding encoding) { return con(ConstantFactory.createFromBytes(value, encoding)); }
public static ValueExpression len(final ValueExpression operand) { return new Len(operand); }
public static NameRef ref(final String name) { return ref(name, null); }
public static NameRef ref(final String name, final ValueExpression limit) { return new NameRef(name, limit); }
public static DefinitionRef ref(final Token definition) { return ref(definition, null); }
public static DefinitionRef ref(final Token definition, final ValueExpression limit) { return new DefinitionRef(definition, limit); }
public static ValueExpression first(final ValueExpression operand) { return new First(operand); }
public static ValueExpression last(final ValueExpression operand) { return new Last(operand); }
public static ValueExpression last(final NameRef operand) { return new Last(new NameRef(operand.reference, con(1))); }
public static ValueExpression last(final DefinitionRef operand) { return new Last(new DefinitionRef(operand.reference, con(1))); }
public static SingleValueExpression last(final ValueExpression operand) { return new Last(operand); }
public static SingleValueExpression last(final NameRef operand) { return new Last(new NameRef(operand.reference, con(1))); }
public static SingleValueExpression last(final DefinitionRef operand) { return new Last(new DefinitionRef(operand.reference, con(1))); }
public static ValueExpression nth(final ValueExpression values, final ValueExpression indices) { return new Nth(values, indices); }
public static ValueExpression offset(final ValueExpression operand) { return new Offset(operand); }
public static ValueExpression iteration(final int level) { return iteration(con(level)); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,16 @@
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.
* <p>
* Const has a single operand <code>value</code> (a {@link Value}). When
* evaluated, this value is returned.
*/
public class Const implements ValueExpression {
public class Const implements SingleValueExpression {

public final Value value;

Expand All @@ -39,8 +38,8 @@ public Const(final Value value) {
}

@Override
public ImmutableList<Optional<Value>> eval(final ParseState parseState, final Encoding encoding) {
return ImmutableList.create(Optional.of(value));
public Optional<Value> evalSingle(ParseState parseState, Encoding encoding) {
return Optional.of(value);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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;

// Suppress warning because what we actually do is in line with error-prone's advice.
@SuppressWarnings("FunctionalInterfaceMethodChanged")
@FunctionalInterface
public interface SingleValueExpression extends ValueExpression {

Optional<Value> evalSingle(ParseState parseState, Encoding encoding);

@Override
default ImmutableList<Optional<Value>> eval(ParseState parseState, Encoding encoding) {
return ImmutableList.create(evalSingle(parseState, encoding));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@
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 <code>operand</code>.
*/
public class Last implements ValueExpression {
public class Last implements SingleValueExpression {

public final ValueExpression operand;

Expand All @@ -41,9 +42,9 @@ public Last(final ValueExpression operand) {
}

@Override
public ImmutableList<Optional<Value>> eval(final ParseState parseState, final Encoding encoding) {
public Optional<Value> evalSingle(final ParseState parseState, final Encoding encoding) {
final ImmutableList<Optional<Value>> list = operand.eval(parseState, encoding);
return list.isEmpty() ? list : ImmutableList.create(list.head);
return list.isEmpty() ? Optional.empty() : list.head;
}

@Override
Expand Down
16 changes: 8 additions & 8 deletions core/src/main/java/io/parsingdata/metal/token/Def.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,40 +29,40 @@

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.SingleValueExpression;
import io.parsingdata.metal.expression.value.Value;
import io.parsingdata.metal.expression.value.ValueExpression;

/**
* A {@link Token} that specifies a value to parse in the input.
* <p>
* A Def consists of a <code>size</code> (a {@link ValueExpression}.
* A Def consists of a <code>size</code> (a {@link SingleValueExpression}.
* <p>
* Parsing will succeed if <code>size</code> evaluates to a single value and if
* Parsing will succeed if <code>size</code> 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
*/
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<ParseState> parseImpl(final Environment environment) {
final ImmutableList<Optional<Value>> sizes = size.eval(environment.parseState, environment.encoding);
if (sizes.size != 1 || !sizes.head.isPresent()) {
final Optional<Value> size = this.size.evalSingle(environment.parseState, environment.encoding);
if (!size.isPresent()) {
return failure();
}
return sizes.head
return size
.filter(dataSize -> dataSize.asNumeric().compareTo(ZERO) != 0)
.map(dataSize -> slice(environment, dataSize.asNumeric()))
.orElseGet(() -> success(environment.parseState));
Expand Down
4 changes: 4 additions & 0 deletions core/src/test/java/io/parsingdata/metal/AutoEqualityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import static io.parsingdata.metal.Shorthand.TRUE;
import static io.parsingdata.metal.Shorthand.con;
import static io.parsingdata.metal.Shorthand.last;
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;
Expand Down Expand Up @@ -85,6 +86,7 @@
import io.parsingdata.metal.expression.value.FoldLeft;
import io.parsingdata.metal.expression.value.FoldRight;
import io.parsingdata.metal.expression.value.Reverse;
import io.parsingdata.metal.expression.value.SingleValueExpression;
import io.parsingdata.metal.expression.value.Value;
import io.parsingdata.metal.expression.value.ValueExpression;
import io.parsingdata.metal.expression.value.arithmetic.Add;
Expand Down Expand Up @@ -146,6 +148,7 @@ public class AutoEqualityTest {
private static final List<Supplier<Object>> TOKENS = Arrays.asList(() -> any("a"), () -> any("b"));
private static final List<Supplier<Object>> 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<Supplier<Object>> VALUE_EXPRESSIONS = Arrays.asList(() -> con(1), () -> con(2));
private static final List<Supplier<Object>> SINGLE_VALUE_EXPRESSIONS = Arrays.asList(() -> con(1), () -> last(con(2)));
private static final List<Supplier<Object>> EXPRESSIONS = Arrays.asList(() -> TRUE, () -> not(TRUE));
private static final List<Supplier<Object>> VALUES = Arrays.asList(() -> ConstantFactory.createFromString("a", enc()), () -> ConstantFactory.createFromString("b", enc()), () -> ConstantFactory.createFromNumeric(1L, signed()));
private static final List<Supplier<Object>> REDUCERS = Arrays.asList(() -> (BinaryOperator<ValueExpression>) Shorthand::cat, () -> (BinaryOperator<ValueExpression>) Shorthand::div);
Expand All @@ -169,6 +172,7 @@ private static Map<Class, List<Supplier<Object>>> buildMap() {
result.put(Token.class, TOKENS);
result.put(Token[].class, TOKEN_ARRAYS);
result.put(ValueExpression.class, VALUE_EXPRESSIONS);
result.put(SingleValueExpression.class, SINGLE_VALUE_EXPRESSIONS);
result.put(Expression.class, EXPRESSIONS);
result.put(Value.class, VALUES);
result.put(BinaryOperator.class, REDUCERS);
Expand Down
5 changes: 3 additions & 2 deletions core/src/test/java/io/parsingdata/metal/ByteLengthTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 =
Expand Down
7 changes: 3 additions & 4 deletions core/src/test/java/io/parsingdata/metal/DefSizeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -48,7 +49,7 @@ public class DefSizeTest {
public static final Token FORMAT =
seq(
def("length", con(4)),
def("data", ref("length"))
def("data", last(ref("length")))
);

@Test
Expand Down Expand Up @@ -78,10 +79,8 @@ public void testInvalidLength() {
}

@Test
public void testEmptyLengthInList() {
public void testEmptyLength() {
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());
}

@Test
Expand Down
9 changes: 5 additions & 4 deletions core/src/test/java/io/parsingdata/metal/ErrorsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static io.parsingdata.metal.Shorthand.con;
import static io.parsingdata.metal.Shorthand.def;
import static io.parsingdata.metal.Shorthand.div;
import static io.parsingdata.metal.Shorthand.last;
import static io.parsingdata.metal.Shorthand.neg;
import static io.parsingdata.metal.Shorthand.ref;
import static io.parsingdata.metal.Shorthand.repn;
Expand Down Expand Up @@ -51,16 +52,16 @@ public class ErrorsTest {
public void noValueForSize() {
thrown = ExpectedException.none();
// Basic division by zero.
final Token token = def("a", div(con(1), con(0)));
final Token token = def("a", last(div(con(1), con(0))));
assertFalse(token.parse(env(stream(1))).isPresent());
// Try to negate division by zero.
final Token token2 = def("a", neg(div(con(1), con(0))));
final Token token2 = def("a", last(neg(div(con(1), con(0)))));
assertFalse(token2.parse(env(stream(1))).isPresent());
// Add one to division by zero.
final Token token3 = def("a", add(div(con(1), con(0)), con(1)));
final Token token3 = def("a", last(add(div(con(1), con(0)), con(1))));
assertFalse(token3.parse(env(stream(1))).isPresent());
// Add division by zero to one.
final Token token4 = def("a", add(con(1), div(con(1), con(0))));
final Token token4 = def("a", last(add(con(1), div(con(1), con(0)))));
assertFalse(token4.parse(env(stream(1))).isPresent());
}

Expand Down
5 changes: 3 additions & 2 deletions core/src/test/java/io/parsingdata/metal/ToStringTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,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.SingleValueExpression;
import io.parsingdata.metal.expression.value.Value;
import io.parsingdata.metal.expression.value.ValueExpression;
import io.parsingdata.metal.token.Token;
Expand Down Expand Up @@ -121,8 +122,8 @@ private Token t() {
return any("a");
}

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()));
private SingleValueExpression v() {
return last(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())));
}

@Test
Expand Down
4 changes: 2 additions & 2 deletions core/src/test/java/io/parsingdata/metal/data/SliceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<ParseState> 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));
Expand Down
Loading

0 comments on commit 94a8d2a

Please sign in to comment.