diff --git a/core/src/main/java/io/parsingdata/metal/Shorthand.java b/core/src/main/java/io/parsingdata/metal/Shorthand.java index 8def88c6..31955d51 100644 --- a/core/src/main/java/io/parsingdata/metal/Shorthand.java +++ b/core/src/main/java/io/parsingdata/metal/Shorthand.java @@ -124,6 +124,38 @@ private Shorthand() {} /** "DEFinition": Instantiates a {@link Def} with {@code size = con(size)} and {@code encoding = null}, nested in a {@link Post}. */ public static Token def(final String name, final long size, final Expression predicate) { return def(name, size, predicate, null); } + + /** "DEFinition": Instantiates a {@link Until} where the size of the def is dynamically determined. */ + public static Token def(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final ValueExpression maxSize, final Token terminator, final Encoding encoding) { return new Until(name, initialSize, stepSize, maxSize, terminator, false, encoding); } + + /** "DEFinition": Instantiates a {@link Until} where the size of the def is dynamically determined with {@code encoding = null}. */ + public static Token def(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final ValueExpression maxSize, final Token terminator) { return def(name, initialSize, stepSize, maxSize, terminator, null); } + + /** "DEFinition": Instantiates a {@link Until} where the size of the def is dynamically determined with {@code maxSize = null}. */ + public static Token def(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final Token terminator, final Encoding encoding) { return def(name, initialSize, stepSize, null, terminator, encoding); } + + /** "DEFinition": Instantiates a {@link Until} where the size of the def is dynamically determined with {@code maxSize = null} and {@code encoding = null}. */ + public static Token def(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final Token terminator) { return def(name, initialSize, stepSize, null, terminator, null); } + + /** "DEFinition": Instantiates a {@link Until} where the size of the def is dynamically determined with {@code stepSize = null} and {@code maxSize = null}. */ + public static Token def(final String name, final ValueExpression initialSize, final Token terminator, final Encoding encoding) { return def(name, initialSize, null, terminator, encoding); } + + /** "DEFinition": Instantiates a {@link Until} where the size of the def is dynamically determined with {@code stepSize = null}, {@code maxSize = null} and {@code encoding = null}. */ + public static Token def(final String name, final ValueExpression initialSize, final Token terminator) { return def(name, initialSize, null, terminator, null); } + + /** "DEFinition": Instantiates a {@link Until} where the size of the def is dynamically determined with {@code initialSize = null}, {@code stepSize = null} and {@code maxSize = null}. */ + public static Token def(final String name, final Token terminator, final Encoding encoding) { return def(name, null, terminator, encoding); } + + /** "DEFinition": Instantiates a {@link Until} where the size of the def is dynamically determined with {@code initialSize = null}, {@code stepSize = null}, {@code maxSize = null} and {@code encoding = null}. */ + public static Token def(final String name, final Token terminator) { return def(name, terminator, null); } + + /** "DEFinition": Instantiates a {@link Until} with the expression nested in a {@link Post} and {@code initialSize = con(1)}. */ + public static Token def(final String name, final Expression predicate, final Encoding encoding) { return def(name, con(1), post(EMPTY, predicate), encoding); } + + /** "DEFinition": Instantiates a {@link Until} where the terminator is the expression nested in a {@link Post}, {@code initialSize = con(1)} and {@code encoding = null}. */ + public static Token def(final String name, final Expression predicate) { return def(name, predicate, null); } + + /** "NO Data": denotes data that is not required during parsing and afterwards. Instantiates a {@link Def} with {@code name = EMPTY_NAME} and {@code encoding = null}. */ public static Token nod(final SingleValueExpression size) { return def(EMPTY_NAME, size); } @@ -180,23 +212,32 @@ private Shorthand() {} /** @see Tie */ public static Token tie(final String name, final Token token, final ValueExpression dataExpression) { return tie(name, token, dataExpression, null); } /** @see Tie */ public static Token tie(final Token token, final ValueExpression dataExpression, final Encoding encoding) { return tie(NO_NAME, token, dataExpression, encoding); } /** @see Tie */ public static Token tie(final Token token, final ValueExpression dataExpression) { return tie(token, dataExpression, null); } - /** @see Until */ public static Token until(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final ValueExpression maxSize, final Token terminator, final Encoding encoding) { return new Until(name, initialSize, stepSize, maxSize, terminator, true, encoding); } - /** @see Until */ public static Token until(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final ValueExpression maxSize, final Token terminator) { return until(name, initialSize, stepSize, maxSize, terminator, null); } - /** @see Until */ public static Token until(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final Token terminator, final Encoding encoding) { return until(name, initialSize, stepSize, null, terminator, encoding); } - /** @see Until */ public static Token until(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final Token terminator) { return until(name, initialSize, stepSize, null, terminator, null); } - /** @see Until */ public static Token until(final String name, final ValueExpression initialSize, final Token terminator, final Encoding encoding) { return until(name, initialSize, null, terminator, encoding); } - /** @see Until */ public static Token until(final String name, final ValueExpression initialSize, final Token terminator) { return until(name, initialSize, null, terminator, null); } - /** @see Until */ public static Token until(final String name, final Token terminator, final Encoding encoding) { return until(name, null, terminator, encoding); } - /** @see Until */ public static Token until(final String name, final Token terminator) { return until(name, terminator, null); } - - public static Token defU(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final ValueExpression maxSize, final Token terminator, final Encoding encoding) { return new Until(name, initialSize, stepSize, maxSize, terminator, false, encoding); } - public static Token defU(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final ValueExpression maxSize, final Token terminator) { return defU(name, initialSize, stepSize, maxSize, terminator, null); } - public static Token defU(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final Token terminator, final Encoding encoding) { return defU(name, initialSize, stepSize, null, terminator, encoding); } - public static Token defU(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final Token terminator) { return defU(name, initialSize, stepSize, null, terminator, null); } - public static Token defU(final String name, final ValueExpression initialSize, final Token terminator, final Encoding encoding) { return defU(name, initialSize, null, terminator, encoding); } - public static Token defU(final String name, final ValueExpression initialSize, final Token terminator) { return defU(name, initialSize, null, terminator, null); } - public static Token defU(final String name, final Token terminator, final Encoding encoding) { return defU(name, null, terminator, encoding); } - public static Token defU(final String name, final Token terminator) { return defU(name, terminator, null); } + + + /** "DEFinition": Instantiates a {@link Until} and its terminator nested in a {@link Seq}. */ + public static Token until(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final ValueExpression maxSize, final Token terminator, final Encoding encoding) { return seq(name, def(name, initialSize, stepSize, maxSize, terminator, encoding), terminator); } + + /** "DEFinition": Instantiates a {@link Until} and its terminator nested in a {@link Seq} with {@code encoding = null}. */ + public static Token until(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final ValueExpression maxSize, final Token terminator) { return until(name, initialSize, stepSize, maxSize, terminator, null); } + + /** "DEFinition": Instantiates a {@link Until} and its terminator nested in a {@link Seq} with {@code maxSize = null}. */ + public static Token until(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final Token terminator, final Encoding encoding) { return until(name, initialSize, stepSize, null, terminator, encoding); } + + /** "DEFinition": Instantiates a {@link Until} and its terminator nested in a {@link Seq} with {@code maxSize = null} and {@code encoding = null}. */ + public static Token until(final String name, final ValueExpression initialSize, final ValueExpression stepSize, final Token terminator) { return until(name, initialSize, stepSize, null, terminator, null); } + + /** "DEFinition": Instantiates a {@link Until} and its terminator nested in a {@link Seq} with {@code stepSize = null} and {@code maxSize = null}. */ + public static Token until(final String name, final ValueExpression initialSize, final Token terminator, final Encoding encoding) { return until(name, initialSize, null, terminator, encoding); } + + /** "DEFinition": Instantiates a {@link Until} and its terminator nested in a {@link Seq} with {@code stepSize = null}, {@code maxSize = null} and {@code encoding = null}. */ + public static Token until(final String name, final ValueExpression initialSize, final Token terminator) { return until(name, initialSize, null, terminator, null); } + + /** "DEFinition": Instantiates a {@link Until} and its terminator nested in a {@link Seq} with {@code initialSize = null}, {@code stepSize = null} and {@code maxSize = null}. */ + public static Token until(final String name, final Token terminator, final Encoding encoding) { return until(name, null, terminator, encoding); } + + /** "DEFinition": Instantiates a {@link Until} and its terminator nested in a {@link Seq} with {@code initialSize = null}, {@code stepSize = null}, {@code maxSize = null} and {@code encoding = null}. */ + public static Token until(final String name, final Token terminator) { return until(name, terminator, null); } + /** "WHEN": denotes a logical implication, parses the {@code token} only if the {@code predicate} evaluates to {code true} and subsequently only fails if {@code token} does not successfully parse. A composition of {@link Cho} and {@link Pre}. */ public static Token when(final String name, final Token token, final Expression predicate, final Encoding encoding) { return cho(name, encoding, pre(def(EMPTY_NAME, 0), not(predicate)), token); } diff --git a/core/src/test/java/io/parsingdata/metal/token/UntilTest.java b/core/src/test/java/io/parsingdata/metal/token/UntilTest.java index 8873ecb5..ad1a45a3 100644 --- a/core/src/test/java/io/parsingdata/metal/token/UntilTest.java +++ b/core/src/test/java/io/parsingdata/metal/token/UntilTest.java @@ -20,7 +20,7 @@ import static io.parsingdata.metal.Shorthand.EMPTY; import static io.parsingdata.metal.Shorthand.con; import static io.parsingdata.metal.Shorthand.def; -import static io.parsingdata.metal.Shorthand.defU; +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.mod; @@ -47,10 +47,10 @@ import io.parsingdata.metal.data.ParseState; import io.parsingdata.metal.data.ParseValue; import io.parsingdata.metal.encoding.Encoding; +import io.parsingdata.metal.expression.Expression; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.runners.Parameterized.Parameters; class UntilTest { @@ -59,12 +59,12 @@ class UntilTest { private static final String INPUT_3 = "Another way to scroll..."; private static final String INPUT = INPUT_1 + "\n" + INPUT_2 + "\n" + INPUT_3 + "\n"; + public static final Expression ENDS_WITH_NEWLINE = eq(mod(last(ref("line")), con(256)), con('\n')); public static final Token NEWLINE = def("newline", con(1), eq(con('\n'))); - public static final Token END_WITH_NEWLINE_POST = post(EMPTY, eq(mod(last(ref("line")), con(256)), con('\n'))); + public static final Token END_WITH_NEWLINE_POST = post(EMPTY, ENDS_WITH_NEWLINE); public static final Token END_WITH_NEWLINE_SUB = sub(NEWLINE, sub(CURRENT_OFFSET, con(1))); public static final Token NEXT_START_WITH_TERMINATOR = sub(NEWLINE, CURRENT_OFFSET); - @Parameters(name="{0}") static Collection data() { return Arrays.asList(new Object[][] { { "until: terminator not part of line, available in parseGraph", until("line", NEWLINE), 3, 3, INPUT_1, INPUT_2, INPUT_3}, @@ -72,16 +72,17 @@ static Collection data() { { "until: terminator part of line, available in parseGraph", until("line", con(1), END_WITH_NEWLINE_SUB), 3, 3, INPUT_1 + '\n', INPUT_2 + '\n', INPUT_3 + '\n'}, { "until: terminator part of next line, available in parseGraph", until("line", con(1), NEXT_START_WITH_TERMINATOR), 3, 3, INPUT_1, '\n' + INPUT_2, '\n' + INPUT_3}, - { "defU: terminator not part of line, available in parseGraph", seq(defU("line", NEWLINE), NEWLINE), 3, 3, INPUT_1, INPUT_2, INPUT_3}, - { "defU: terminator part of line, not available in parseGraph", defU("line", con(1), END_WITH_NEWLINE_POST), 3, 0, INPUT_1 + '\n', INPUT_2 + '\n', INPUT_3 + '\n'}, - { "defU: terminator part of line, not available in parseGraph", defU("line", con(1), END_WITH_NEWLINE_SUB), 3, 0, INPUT_1 + '\n', INPUT_2 + '\n', INPUT_3 + '\n'}, - { "defU: terminator part of next line, not available in parseGraph", defU("line", con(1), NEWLINE), 3, 0, INPUT_1, '\n' + INPUT_2, '\n' + INPUT_3}, - { "defU: terminator part of next line, not available in parseGraph", defU("line", con(1), NEXT_START_WITH_TERMINATOR), 3, 0, INPUT_1, '\n' + INPUT_2, '\n' + INPUT_3}, - // "defU: terminator part of line, available in parseGraph" is not possible with defU, only with until. + { "def: terminator not part of line, available in parseGraph", seq(def("line", NEWLINE), NEWLINE), 3, 3, INPUT_1, INPUT_2, INPUT_3}, + { "def: terminator part of line, not available in parseGraph", def("line", con(1), END_WITH_NEWLINE_POST), 3, 0, INPUT_1 + '\n', INPUT_2 + '\n', INPUT_3 + '\n'}, + { "def: terminator part of line, not available in parseGraph", def("line", con(1), END_WITH_NEWLINE_SUB), 3, 0, INPUT_1 + '\n', INPUT_2 + '\n', INPUT_3 + '\n'}, + { "def: terminator part of line, not available in parseGraph", def("line", ENDS_WITH_NEWLINE), 3, 0, INPUT_1 + '\n', INPUT_2 + '\n', INPUT_3 + '\n'}, + { "def: terminator part of next line, not available in parseGraph", def("line", con(1), NEWLINE), 3, 0, INPUT_1, '\n' + INPUT_2, '\n' + INPUT_3}, + { "def: terminator part of next line, not available in parseGraph", def("line", con(1), NEXT_START_WITH_TERMINATOR), 3, 0, INPUT_1, '\n' + INPUT_2, '\n' + INPUT_3}, + // "def: terminator part of line, available in parseGraph" is not possible with def, only with until. }); } - @ParameterizedTest + @ParameterizedTest(name="{0}") @MethodSource("data") void repTest(final String name, final Token token, final int lineCount, final int newlineCount, final String line1, final String line2, final String line3) { final Optional parseState = rep(token).parse(env(stream(INPUT, US_ASCII))); @@ -97,22 +98,21 @@ void repTest(final String name, final Token token, final int lineCount, final in assertEquals(newlineCount, newLines.size); } - @Parameters(name="{0}") - static Collection defUShorthands() { + static Collection shorthandTokenTest() { return Arrays.asList(new Object[][] { - { "defU", defU("line", NEWLINE) }, - { "defU initial size", defU("line", con(13), NEWLINE)}, - { "defU initial size + encoding", defU("line", con(13), NEWLINE, Encoding.DEFAULT_ENCODING)}, - { "defU step size", defU("line", con(13), con(1), NEWLINE)}, - { "defU step size + encoding", defU("line", con(13), con(1), NEWLINE, Encoding.DEFAULT_ENCODING)}, - { "defU max size", defU("line", con(13), con(1), con(24), NEWLINE)}, - { "defU max size + encoding", defU("line", con(13), con(1), con(24), NEWLINE, Encoding.DEFAULT_ENCODING)}, + { "def", def("line", NEWLINE) }, + { "def initial size", def("line", con(13), NEWLINE)}, + { "def initial size + encoding", def("line", con(13), NEWLINE, Encoding.DEFAULT_ENCODING)}, + { "def step size", def("line", con(13), con(1), NEWLINE)}, + { "def step size + encoding", def("line", con(13), con(1), NEWLINE, Encoding.DEFAULT_ENCODING)}, + { "def max size", def("line", con(13), con(1), con(24), NEWLINE)}, + { "def max size + encoding", def("line", con(13), con(1), con(24), NEWLINE, Encoding.DEFAULT_ENCODING)}, }); } - @ParameterizedTest - @MethodSource("defUShorthands") - void shorthandTest(final String name, final Token token) { + @ParameterizedTest(name="{0}") + @MethodSource + void shorthandTokenTest(final String name, final Token token) { final Optional parseState = rep(seq(token, NEWLINE)).parse(env(stream(INPUT, US_ASCII))); assertTrue(parseState.isPresent()); @@ -126,6 +126,30 @@ void shorthandTest(final String name, final Token token) { assertEquals(3, newLines.size); } + static Collection shorthandExpressionTest() { + return Arrays.asList(new Object[][] { + { "def token", def("line", con(1), END_WITH_NEWLINE_POST)}, + { "def expression", def("line", ENDS_WITH_NEWLINE)}, + { "def expression + encoding", def("line", ENDS_WITH_NEWLINE, Encoding.DEFAULT_ENCODING)}, + }); + } + + @ParameterizedTest(name="{0}") + @MethodSource + void shorthandExpressionTest(final String name, final Token token) { + final Optional parseState = rep(token).parse(env(stream(INPUT, US_ASCII))); + assertTrue(parseState.isPresent()); + + ImmutableList values = getAllValues(parseState.get().order, "line"); + assertEquals(3, values.size); + assertEquals(INPUT_1 + "\n", values.tail.tail.head.asString()); + assertEquals(INPUT_2 + "\n", values.tail.head.asString()); + assertEquals(INPUT_3 + "\n", values.head.asString()); + + ImmutableList newLines = getAllValues(parseState.get().order, "newline"); + assertEquals(0, newLines.size); + } + @Test void allDefaultValueExpressions() { assertTrue(until("value", def("terminator", 1, eq(con(0)))).parse(env(stream(1, 2, 3, 0))).isPresent());