initial commit
This commit is contained in:
13
java/expression/parser/ListParser.java
Normal file
13
java/expression/parser/ListParser.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package expression.parser;
|
||||
|
||||
import expression.ListExpression;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ListParser {
|
||||
ListExpression parse(String expression, List<String> variables);
|
||||
}
|
||||
148
java/expression/parser/Operations.java
Normal file
148
java/expression/parser/Operations.java
Normal file
@@ -0,0 +1,148 @@
|
||||
package expression.parser;
|
||||
|
||||
import expression.ToMiniString;
|
||||
import expression.common.ExpressionKind;
|
||||
import expression.common.Reason;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
/**
|
||||
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||
*/
|
||||
public final class Operations {
|
||||
// === Base
|
||||
|
||||
public static final Operation NEGATE = unary("-", 1, a -> -a);
|
||||
@SuppressWarnings("Convert2MethodRef")
|
||||
public static final Operation ADD = binary("+", 1600, (a, b) -> a + b);
|
||||
public static final Operation SUBTRACT = binary("-", 1602, (a, b) -> a - b);
|
||||
public static final Operation MULTIPLY = binary("*", 2001, (a, b) -> a * b);
|
||||
public static final Operation DIVIDE = binary("/", 2002, (a, b) -> b == 0 ? Reason.DBZ.error() : a / b);
|
||||
|
||||
|
||||
// === MinMax
|
||||
public static final Operation MIN = binary("min", 401, Math::min);
|
||||
public static final Operation MAX = binary("max", 401, Math::max);
|
||||
|
||||
|
||||
// === Reverse
|
||||
|
||||
private static Operation digits(final String name, final boolean mask, final int r, final LongBinaryOperator q) {
|
||||
return unary(name, 1, v -> LongStream.iterate(mask ? v & 0xffff_ffffL : v, n -> n != 0, n -> n / r)
|
||||
.map(n -> n % r)
|
||||
.reduce(0, q));
|
||||
}
|
||||
|
||||
public static final Operation REVERSE = digits("reverse", false, 10, (a, b) -> a * 10 + b);
|
||||
|
||||
|
||||
// === Digits
|
||||
public static final Operation DIGITS = digits("digits", false, 10, Long::sum);
|
||||
|
||||
|
||||
// === Floor and Ceiling
|
||||
|
||||
private static long floor(final long a) {
|
||||
return (a >= 0 ? a : a - FLOOR_CEILING_STEP + 1) / FLOOR_CEILING_STEP * FLOOR_CEILING_STEP;
|
||||
}
|
||||
|
||||
private static long ceiling(final long a) {
|
||||
return (a >= 0 ? a + FLOOR_CEILING_STEP - 1: a) / FLOOR_CEILING_STEP * FLOOR_CEILING_STEP;
|
||||
}
|
||||
|
||||
public static final int FLOOR_CEILING_STEP = 1000;
|
||||
public static final Operation FLOOR = unary("floor", 1, Operations::floor);
|
||||
public static final Operation CEILING = unary("ceiling", 1, Operations::ceiling);
|
||||
|
||||
// === Set, Clear
|
||||
|
||||
@SuppressWarnings("IntegerMultiplicationImplicitCastToLong")
|
||||
public static final Operation SET = binary("set", 202, (a, b) -> a | (1 << b));
|
||||
@SuppressWarnings("IntegerMultiplicationImplicitCastToLong")
|
||||
public static final Operation CLEAR = binary("clear", 202, (a, b) -> a & ~(1 << b));
|
||||
|
||||
|
||||
// === Pow, Log
|
||||
public static final Operation POW_O = binary("**", 2402, (a, b) ->
|
||||
b < 0 ? 1 : BigInteger.valueOf(a).modPow(BigInteger.valueOf(b), BigInteger.valueOf(1L << 32)).intValue());
|
||||
public static final Operation LOG_O = binary("//", 2402, (a, b) ->
|
||||
a == 0 && b > 0 ? Integer.MIN_VALUE :
|
||||
a <= 0 || b <= 0 || a == 1 && b == 1 ? 0 :
|
||||
a > 1 && b == 1 ? Integer.MAX_VALUE
|
||||
: LongStream.iterate(b, v -> v <= a, v -> v * b).count()
|
||||
);
|
||||
|
||||
private static final Reason INVALID_POW = new Reason("Invalid power");
|
||||
public static final Operation POW = binary("**", 2402, Operations::powC);
|
||||
|
||||
private static long powC(final long a, final long b) {
|
||||
if (b < 0 || a == 0 && b == 0) {
|
||||
return INVALID_POW.error();
|
||||
}
|
||||
if (Math.abs(a) > 1 && b > 32) {
|
||||
return Reason.OVERFLOW.error();
|
||||
}
|
||||
final BigInteger result = BigInteger.valueOf(a).pow((int) b);
|
||||
if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || BigInteger.valueOf(Integer.MAX_VALUE).compareTo(result) < 0) {
|
||||
return Reason.OVERFLOW.error();
|
||||
}
|
||||
return result.intValue();
|
||||
}
|
||||
|
||||
private static final Reason INVALID_LOG = new Reason("Invalid log");
|
||||
public static final Operation LOG = binary("//", 2402, (a, b) ->
|
||||
a <= 0 || b <= 1 ? INVALID_LOG.error() : (int) (Math.log(a) / Math.log(b)));
|
||||
|
||||
|
||||
// Pow2, Log2
|
||||
|
||||
private static final Reason NEG_LOG = new Reason("Logarithm of negative value");
|
||||
public static final Operation LOG_2
|
||||
= unary("log₂", 1, NEG_LOG.less(1, a-> (long) (Math.log(a) / Math.log(2))));
|
||||
|
||||
private static final Reason NEG_POW = new Reason("Exponentiation to negative power");
|
||||
public static final Operation POW_2
|
||||
= unary("pow₂", 1, NEG_POW.less(0, Reason.OVERFLOW.greater(31, a -> (long) Math.pow(2, a))));
|
||||
|
||||
|
||||
// === High, Low
|
||||
public static final Operation HIGH = unary("high", 1, v -> Integer.highestOneBit((int) v));
|
||||
public static final Operation LOW = unary("low", 1, v -> Integer.lowestOneBit((int) v));
|
||||
|
||||
// === Common
|
||||
|
||||
private Operations() {
|
||||
}
|
||||
|
||||
public static Operation unary(final String name, final int priority, final LongUnaryOperator op) {
|
||||
return unary(name, priority, (a, c) -> op.applyAsLong(a));
|
||||
}
|
||||
|
||||
public static Operation unary(final String left, final String right, final LongUnaryOperator op) {
|
||||
return unary(left, right, (a, c) -> op.applyAsLong(a));
|
||||
}
|
||||
|
||||
public static Operation unary(final String name, final int priority, final BiFunction<Long, LongToIntFunction, Long> op) {
|
||||
return tests -> tests.unary(name, priority, op);
|
||||
}
|
||||
|
||||
public static Operation unary(final String left, final String right, final BiFunction<Long, LongToIntFunction, Long> op) {
|
||||
return tests -> tests.unary(left, right, op);
|
||||
}
|
||||
|
||||
public static Operation binary(final String name, final int priority, final LongBinaryOperator op) {
|
||||
return tests -> tests.binary(name, priority, op);
|
||||
}
|
||||
|
||||
public static <E extends ToMiniString, C> Operation kind(
|
||||
final ExpressionKind<E, C> kind,
|
||||
final ParserTestSet.Parser<E> parser
|
||||
) {
|
||||
return factory -> factory.kind(kind, parser);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Operation extends Consumer<ParserTester> {}
|
||||
}
|
||||
31
java/expression/parser/ParserTest.java
Normal file
31
java/expression/parser/ParserTest.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package expression.parser;
|
||||
|
||||
import base.Selector;
|
||||
import expression.ListExpression;
|
||||
|
||||
import static expression.parser.Operations.*;
|
||||
|
||||
/**
|
||||
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||
*/
|
||||
public final class ParserTest {
|
||||
private static final ExpressionParser PARSER = new ExpressionParser();
|
||||
private static final Operations.Operation LIST = kind(ListExpression.KIND, PARSER::parse);
|
||||
|
||||
// === Common
|
||||
|
||||
public static final Selector SELECTOR = Selector.composite(ParserTest.class, ParserTester::new, "easy", "hard")
|
||||
.variant("Base", LIST, ADD, SUBTRACT, MULTIPLY, DIVIDE, NEGATE)
|
||||
.variant("3637", MIN, MAX, REVERSE)
|
||||
.variant("3839", MIN, MAX, REVERSE, DIGITS)
|
||||
.variant("3435", FLOOR, CEILING, SET, CLEAR)
|
||||
.variant("3233", FLOOR, CEILING)
|
||||
.selector();
|
||||
|
||||
private ParserTest() {
|
||||
}
|
||||
|
||||
public static void main(final String... args) {
|
||||
SELECTOR.main(args);
|
||||
}
|
||||
}
|
||||
236
java/expression/parser/ParserTestSet.java
Normal file
236
java/expression/parser/ParserTestSet.java
Normal file
@@ -0,0 +1,236 @@
|
||||
package expression.parser;
|
||||
|
||||
import base.*;
|
||||
import expression.ToMiniString;
|
||||
import expression.common.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||
*/
|
||||
public class ParserTestSet<E extends ToMiniString, C> {
|
||||
private static final int D = 5;
|
||||
|
||||
private static final List<Integer> TEST_VALUES = new ArrayList<>();
|
||||
static {
|
||||
Functional.addRange(TEST_VALUES, D, D);
|
||||
Functional.addRange(TEST_VALUES, D, -D);
|
||||
}
|
||||
|
||||
public static final List<Integer> CONSTS
|
||||
= List.of(0, 1, -1, 4, -4, 10, -10, 30, -30, 100, -100, Integer.MAX_VALUE, Integer.MIN_VALUE);
|
||||
|
||||
protected final ParserTester tester;
|
||||
protected final ParsedKind<E, C> kind;
|
||||
private final boolean safe;
|
||||
|
||||
protected final TestCounter counter;
|
||||
|
||||
public ParserTestSet(final ParserTester tester, final ParsedKind<E, C> kind) {
|
||||
this(tester, kind, true);
|
||||
}
|
||||
|
||||
protected ParserTestSet(final ParserTester tester, final ParsedKind<E, C> kind, final boolean safe) {
|
||||
this.tester = tester;
|
||||
this.kind = kind;
|
||||
this.safe = safe;
|
||||
|
||||
counter = tester.getCounter();
|
||||
}
|
||||
|
||||
private void examples(final TestGenerator<Integer, E> generator) {
|
||||
example(generator, "$x+2", (x, y, z) -> x + 2);
|
||||
example(generator, "2-$y", (x, y, z) -> 2 - y);
|
||||
example(generator, " 3* $z ", (x, y, z) -> 3 * z);
|
||||
example(generator, "$x/ - 2", (x, y, z) -> -x / 2);
|
||||
example(generator, "$x*$y+($z-1 )/10", (x, y, z) -> x * y + (int) (z - 1) / 10);
|
||||
example(generator, "-(-(-\t\t-5 + 16 *$x*$y) + 1 * $z) -(((-11)))", (x, y, z) -> -(-(5 + 16 * x * y) + z) + 11);
|
||||
example(generator, "" + Integer.MAX_VALUE, (x, y, z) -> (long) Integer.MAX_VALUE);
|
||||
example(generator, "" + Integer.MIN_VALUE, (x, y, z) -> (long) Integer.MIN_VALUE);
|
||||
example(generator, "$x--$y--$z", (x, y, z) -> x + y + z);
|
||||
example(generator, "((2+2))-0/(--2)*555", (x, y, z) -> 4L);
|
||||
example(generator, "$x-$x+$y-$y+$z-($z)", (x, y, z) -> 0L);
|
||||
example(generator, "(".repeat(300) + "$x + $y + (-10*-$z)" + ")".repeat(300), (x, y, z) -> x + y + 10 * z);
|
||||
example(generator, "$x / $y / $z", (x, y, z) -> y == 0 || z == 0 ? Reason.DBZ.error() : (int) x / (int) y / z);
|
||||
}
|
||||
|
||||
private void example(final TestGenerator<Integer, E> generator, final String expr, final ExampleExpression expression) {
|
||||
final List<String> names = Functional.map(generator.variables(3), Pair::first);
|
||||
final TExpression expected = vars -> expression.evaluate(vars.get(0), vars.get(1), vars.get(2));
|
||||
|
||||
counter.test(() -> {
|
||||
final String mangled = mangle(expr, names);
|
||||
final E parsed = parse(mangled, names, true);
|
||||
Functional.allValues(TEST_VALUES, 3).forEach(values -> check(expected, parsed, names, values, mangled));
|
||||
});
|
||||
}
|
||||
|
||||
protected static String mangle(final String expr, final List<String> names) {
|
||||
return expr
|
||||
.replace("$x", names.get(0))
|
||||
.replace("$y", names.get(1))
|
||||
.replace("$z", names.get(2));
|
||||
}
|
||||
|
||||
protected void test() {
|
||||
final TestGenerator<Integer, E> generator = tester.generator.build(kind.kind.variables());
|
||||
final Renderer<Integer, Unit, TExpression> renderer = tester.renderer.build();
|
||||
final Consumer<TestGenerator.Test<Integer, E>> consumer = test -> test(renderer, test);
|
||||
counter.scope("Basic tests", () -> generator.testBasic(consumer));
|
||||
counter.scope("Handmade tests", () -> examples(generator));
|
||||
counter.scope("Random tests", () -> generator.testRandom(counter, 1, consumer));
|
||||
}
|
||||
|
||||
private void test(final Renderer<Integer, Unit, TExpression> renderer, final TestGenerator.Test<Integer, E> test) {
|
||||
final Expr<Integer, E> expr = test.expr;
|
||||
final List<Pair<String, E>> vars = expr.variables();
|
||||
final List<String> variables = Functional.map(vars, Pair::first);
|
||||
final String full = test.render(NodeRenderer.FULL);
|
||||
final String mini = test.render(NodeRenderer.MINI);
|
||||
|
||||
final E fullParsed = parse(test, variables, NodeRenderer.FULL);
|
||||
final E miniParsed = parse(test, variables, NodeRenderer.MINI);
|
||||
final E safeParsed = parse(test, variables, NodeRenderer.SAME);
|
||||
|
||||
checkToString(full, mini, "base", fullParsed);
|
||||
if (tester.mode() > 0) {
|
||||
counter.test(() -> Asserts.assertEquals("mini.toMiniString", mini, miniParsed.toMiniString()));
|
||||
counter.test(() -> Asserts.assertEquals("safe.toMiniString", mini, safeParsed.toMiniString()));
|
||||
}
|
||||
checkToString(full, mini, "extraParentheses", parse(test, variables, NodeRenderer.FULL_EXTRA));
|
||||
checkToString(full, mini, "noSpaces", parse(removeSpaces(full), variables, false));
|
||||
checkToString(full, mini, "extraSpaces", parse(extraSpaces(full), variables, false));
|
||||
|
||||
final TExpression expected = renderer.render(
|
||||
Expr.of(
|
||||
expr.node(),
|
||||
Functional.map(vars, (i, var) -> Pair.of(var.first(), args -> args.get(i)))
|
||||
),
|
||||
Unit.INSTANCE
|
||||
);
|
||||
|
||||
check(expected, fullParsed, variables, tester.random().random(variables.size(), ExtendedRandom::nextInt), full);
|
||||
if (this.safe) {
|
||||
final String safe = test.render(NodeRenderer.SAME);
|
||||
check(expected, safeParsed, variables, tester.random().random(variables.size(), ExtendedRandom::nextInt), safe);
|
||||
}
|
||||
}
|
||||
|
||||
private E parse(
|
||||
final TestGenerator.Test<Integer, E> test,
|
||||
final List<String> variables,
|
||||
final NodeRenderer.Settings settings
|
||||
) {
|
||||
return parse(test.render(settings.withParens(tester.parens)), variables, false);
|
||||
}
|
||||
|
||||
private static final String LOOKBEHIND = "(?<![a-zA-Z0-9<>*/+=!-])";
|
||||
private static final String LOOKAHEAD = "(?![a-zA-Z0-9<>*/])";
|
||||
private static final Pattern SPACES = Pattern.compile(LOOKBEHIND + " | " + LOOKAHEAD + "|" + LOOKAHEAD + LOOKBEHIND);
|
||||
private String extraSpaces(final String expression) {
|
||||
return SPACES.matcher(expression).replaceAll(r -> tester.random().randomString(
|
||||
ExtendedRandom.SPACES,
|
||||
tester.random().nextInt(5)
|
||||
));
|
||||
}
|
||||
|
||||
private static String removeSpaces(final String expression) {
|
||||
return SPACES.matcher(expression).replaceAll("");
|
||||
}
|
||||
|
||||
private void checkToString(final String full, final String mini, final String context, final ToMiniString parsed) {
|
||||
counter.test(() -> {
|
||||
assertEquals(context + ".toString", full, full, parsed.toString());
|
||||
if (tester.mode() > 0) {
|
||||
assertEquals(context + ".toMiniString", full, mini, parsed.toMiniString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void assertEquals(
|
||||
final String context,
|
||||
final String original,
|
||||
final String expected,
|
||||
final String actual
|
||||
) {
|
||||
final String message = String.format("%s:%n original `%s`,%n expected `%s`,%n actual `%s`",
|
||||
context, original, expected, actual);
|
||||
Asserts.assertTrue(message, Objects.equals(expected, actual));
|
||||
}
|
||||
|
||||
private Either<Reason, Integer> eval(final TExpression expression, final List<Integer> vars) {
|
||||
return Reason.eval(() -> tester.cast(expression.evaluate(vars)));
|
||||
}
|
||||
|
||||
protected E parse(final String expression, final List<String> variables, final boolean reparse) {
|
||||
return counter.testV(() -> {
|
||||
final E parsed = counter.testV(() -> counter.call("parse",
|
||||
() -> kind.parse(expression, variables)));
|
||||
if (reparse) {
|
||||
counter.testV(() -> counter.call("parse", () -> kind.parse(parsed.toString(), variables)));
|
||||
}
|
||||
return parsed;
|
||||
});
|
||||
}
|
||||
|
||||
private void check(
|
||||
final TExpression expectedExpression,
|
||||
final E expression,
|
||||
final List<String> variables,
|
||||
final List<Integer> values,
|
||||
final String unparsed
|
||||
) {
|
||||
counter.test(() -> {
|
||||
final Either<Reason, Integer> answer = eval(expectedExpression, values);
|
||||
final String args = IntStream.range(0, variables.size())
|
||||
.mapToObj(i -> variables.get(i) + "=" + values.get(i))
|
||||
.collect(Collectors.joining(", "));
|
||||
final String message = String.format("f(%s)%n\twhere f=%s%n\tyour f=%s", args, unparsed, expression);
|
||||
try {
|
||||
final C actual = kind.kind.evaluate(expression, variables, kind.kind.fromInts(values));
|
||||
counter.checkTrue(answer.isRight(), "Error expected for f(%s)%n\twhere f=%s%n\tyour f=%s", args, unparsed, expression);
|
||||
Asserts.assertEquals(message, answer.getRight(), actual);
|
||||
} catch (final Exception e) {
|
||||
if (answer.isRight()) {
|
||||
counter.fail(e, "No error expected for %s", message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TExpression {
|
||||
long evaluate(List<Integer> vars);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface ExampleExpression {
|
||||
long evaluate(long x, long y, long z);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||
*/
|
||||
public record ParsedKind<E extends ToMiniString, C>(ExpressionKind<E, C> kind, Parser<E> parser) {
|
||||
public E parse(final String expression, final List<String> variables) throws Exception {
|
||||
return parser.parse(expression, variables);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return kind.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Parser<E> {
|
||||
E parse(final String expression, final List<String> variables) throws Exception;
|
||||
}
|
||||
}
|
||||
76
java/expression/parser/ParserTester.java
Normal file
76
java/expression/parser/ParserTester.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package expression.parser;
|
||||
|
||||
import base.ExtendedRandom;
|
||||
import base.TestCounter;
|
||||
import base.Tester;
|
||||
import base.Unit;
|
||||
import expression.ToMiniString;
|
||||
import expression.common.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.LongBinaryOperator;
|
||||
import java.util.function.LongToIntFunction;
|
||||
|
||||
/**
|
||||
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||
*/
|
||||
public class ParserTester extends Tester {
|
||||
/* package-private */ final TestGeneratorBuilder<Integer> generator;
|
||||
/* package-private */ final Renderer.Builder<Integer, Unit, ParserTestSet.TExpression> renderer;
|
||||
private final List<ParserTestSet.ParsedKind<?, ?>> kinds = new ArrayList<>();
|
||||
/* package-private */ final List<NodeRenderer.Paren> parens = new ArrayList<>(List.of(NodeRenderer.paren("(", ")")));
|
||||
|
||||
public ParserTester(final TestCounter counter) {
|
||||
super(counter);
|
||||
renderer = Renderer.builder(c -> vars -> c);
|
||||
final ExtendedRandom random = counter.random();
|
||||
generator = new TestGeneratorBuilder<>(random, random::nextInt, ParserTestSet.CONSTS, true);
|
||||
}
|
||||
|
||||
public void unary(final String name, final int priority, final BiFunction<Long, LongToIntFunction, Long> op) {
|
||||
generator.unary(name, priority);
|
||||
renderer.unary(name, (unit, a) -> vars -> cast(op.apply(a.evaluate(vars), this::cast)));
|
||||
}
|
||||
|
||||
public void unary(final String left, final String right, final BiFunction<Long, LongToIntFunction, Long> op) {
|
||||
generator.unary(left, right);
|
||||
renderer.unary(left, (unit, a) -> vars -> cast(op.apply(a.evaluate(vars), this::cast)));
|
||||
}
|
||||
|
||||
public void binary(final String name, final int priority, final LongBinaryOperator op) {
|
||||
generator.binary(name, priority);
|
||||
renderer.binary(name, (unit, a, b) -> vars -> cast(op.applyAsLong(a.evaluate(vars), b.evaluate(vars))));
|
||||
}
|
||||
|
||||
<E extends ToMiniString, C> void kind(final ExpressionKind<E, C> kind, final ParserTestSet.Parser<E> parser) {
|
||||
kinds.add(new ParserTestSet.ParsedKind<>(kind, parser));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void test() {
|
||||
for (final ParserTestSet.ParsedKind<?, ?> kind : kinds) {
|
||||
counter.scope(kind.toString(), () -> test(kind));
|
||||
}
|
||||
}
|
||||
|
||||
protected void test(final ParserTestSet.ParsedKind<?, ?> kind) {
|
||||
new ParserTestSet<>(this, kind).test();
|
||||
}
|
||||
|
||||
public TestCounter getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
protected int cast(final long value) {
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
public void parens(final String... parens) {
|
||||
assert parens.length % 2 == 0 : "Parens should come in pairs";
|
||||
for (int i = 0; i < parens.length; i += 2) {
|
||||
this.parens.add(NodeRenderer.paren(parens[i], parens[i + 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
7
java/expression/parser/package-info.java
Normal file
7
java/expression/parser/package-info.java
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Tests for <a href="https://www.kgeorgiy.info/courses/prog-intro/homeworks.html#expressions-parsing">Expressions Parsing</a> homework
|
||||
* of <a href="https://www.kgeorgiy.info/courses/prog-intro/">Introduction to Programming</a> course.
|
||||
*
|
||||
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||
*/
|
||||
package expression.parser;
|
||||
Reference in New Issue
Block a user