Upload files to "java/expression/common"
This commit is contained in:
201
java/expression/common/NodeRendererBuilder.java
Normal file
201
java/expression/common/NodeRendererBuilder.java
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
package expression.common;
|
||||||
|
|
||||||
|
import base.ExtendedRandom;
|
||||||
|
import base.Functional;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("StaticMethodOnlyUsedInOneClass")
|
||||||
|
public class NodeRendererBuilder<C> {
|
||||||
|
|
||||||
|
private final Renderer.Builder<
|
||||||
|
C,
|
||||||
|
NodeRenderer.Settings,
|
||||||
|
Node<C>
|
||||||
|
> nodeRenderer = Renderer.builder(Node::constant);
|
||||||
|
private final Map<String, Priority> priorities = new HashMap<>();
|
||||||
|
private final Map<String, String> brackets = new HashMap<>();
|
||||||
|
private final ExtendedRandom random;
|
||||||
|
|
||||||
|
public NodeRendererBuilder(final ExtendedRandom random) {
|
||||||
|
this.random = random;
|
||||||
|
nodeRenderer.unary(NodeRenderer.PAREN, (mode, arg) ->
|
||||||
|
NodeRenderer.paren(true, arg)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unary(final String name, final int priority) {
|
||||||
|
final String space =
|
||||||
|
name.equals("-") || Character.isLetter(name.charAt(0)) ? " " : "";
|
||||||
|
nodeRenderer.unary(name, (settings, arg) ->
|
||||||
|
settings.extra(
|
||||||
|
Node.op(name, priority, inner(settings, priority, arg, space)),
|
||||||
|
random
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unary(final String left, final String right) {
|
||||||
|
brackets.put(left, right);
|
||||||
|
nodeRenderer.unary(left, (settings, arg) ->
|
||||||
|
settings.extra(Node.op(left, Integer.MAX_VALUE, arg), random)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node<C> inner(
|
||||||
|
final NodeRenderer.Settings settings,
|
||||||
|
final int priority,
|
||||||
|
final Node<C> arg,
|
||||||
|
final String space
|
||||||
|
) {
|
||||||
|
if (settings.mode() == NodeRenderer.Mode.FULL) {
|
||||||
|
return NodeRenderer.paren(true, arg);
|
||||||
|
} else {
|
||||||
|
final String op = arg.get(
|
||||||
|
c -> space,
|
||||||
|
n -> space,
|
||||||
|
(n, p, a) ->
|
||||||
|
priority > unaryPriority(arg)
|
||||||
|
? NodeRenderer.PAREN
|
||||||
|
: NodeRenderer.PAREN.equals(n)
|
||||||
|
? ""
|
||||||
|
: space,
|
||||||
|
(n, a, b) -> NodeRenderer.PAREN
|
||||||
|
);
|
||||||
|
return op.isEmpty()
|
||||||
|
? arg
|
||||||
|
: Node.op(op, Priority.MAX.priority | 1, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <C> Integer unaryPriority(final Node<C> node) {
|
||||||
|
return node.get(
|
||||||
|
c -> Integer.MAX_VALUE,
|
||||||
|
n -> Integer.MAX_VALUE,
|
||||||
|
(n, p, a) -> p,
|
||||||
|
(n, a, b) -> Integer.MIN_VALUE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void binary(final String name, final int priority) {
|
||||||
|
final Priority mp = new Priority(name, priority);
|
||||||
|
priorities.put(name, mp);
|
||||||
|
|
||||||
|
nodeRenderer.binary(name, (settings, l, r) ->
|
||||||
|
settings.extra(process(settings, mp, l, r), random)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node<C> process(
|
||||||
|
final NodeRenderer.Settings settings,
|
||||||
|
final Priority mp,
|
||||||
|
final Node<C> l,
|
||||||
|
final Node<C> r
|
||||||
|
) {
|
||||||
|
if (settings.mode() == NodeRenderer.Mode.FULL) {
|
||||||
|
return NodeRenderer.paren(true, op(mp, l, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Priority lp = priority(l);
|
||||||
|
final Priority rp = priority(r);
|
||||||
|
|
||||||
|
final int rc = rp.compareLevels(mp);
|
||||||
|
|
||||||
|
// :NOTE: Especially ugly code, do not replicate
|
||||||
|
final boolean advanced =
|
||||||
|
settings.mode() == NodeRenderer.Mode.SAME ||
|
||||||
|
mp.has(2) ||
|
||||||
|
(mp.has(1) &&
|
||||||
|
(mp != rp ||
|
||||||
|
(settings.mode() == NodeRenderer.Mode.TRUE_MINI &&
|
||||||
|
hasOther(r, rp))));
|
||||||
|
|
||||||
|
final Node<C> al = NodeRenderer.paren(lp.compareLevels(mp) < 0, l);
|
||||||
|
if (rc == 0 && !advanced) {
|
||||||
|
return get(r, null, (n, a, b) -> rp.op(mp.op(al, a), b));
|
||||||
|
} else {
|
||||||
|
return mp.op(
|
||||||
|
al,
|
||||||
|
NodeRenderer.paren((rc == 0 && advanced) || rc < 0, r)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasOther(final Node<C> node, final Priority priority) {
|
||||||
|
return get(
|
||||||
|
node,
|
||||||
|
() -> false,
|
||||||
|
(name, l, r) -> {
|
||||||
|
final Priority p = Functional.get(priorities, name);
|
||||||
|
if (p.compareLevels(priority) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return p != priority || hasOther(l, priority);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node<C> op(final Priority mp, final Node<C> l, final Node<C> r) {
|
||||||
|
return mp.op(l, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Priority priority(final Node<C> node) {
|
||||||
|
return get(
|
||||||
|
node,
|
||||||
|
() -> Priority.MAX,
|
||||||
|
(n, a, b) -> Functional.get(priorities, n)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <R> R get(
|
||||||
|
final Node<C> node,
|
||||||
|
final Supplier<R> common,
|
||||||
|
final Node.Binary<Node<C>, R> binary
|
||||||
|
) {
|
||||||
|
return node.get(
|
||||||
|
c -> common.get(),
|
||||||
|
n -> common.get(),
|
||||||
|
(n, p, a) -> common.get(),
|
||||||
|
binary
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeRenderer<C> build() {
|
||||||
|
return new NodeRenderer<>(nodeRenderer.build(), brackets, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
// :NOTE: Especially ugly bit-fiddling, do not replicate
|
||||||
|
private record Priority(String op, int priority) {
|
||||||
|
private static final int Q = 3;
|
||||||
|
private static final Priority MAX = new Priority(
|
||||||
|
"MAX",
|
||||||
|
Integer.MAX_VALUE - Q
|
||||||
|
);
|
||||||
|
|
||||||
|
private int compareLevels(final Priority that) {
|
||||||
|
return (priority | Q) - (that.priority | Q);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format(
|
||||||
|
"Priority(%s, %d, %d)",
|
||||||
|
op,
|
||||||
|
priority | Q,
|
||||||
|
priority & Q
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <C> Node<C> op(final Node<C> l, final Node<C> r) {
|
||||||
|
return Node.op(op, l, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean has(final int value) {
|
||||||
|
return (priority & Q) == value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
java/expression/common/Reason.java
Normal file
67
java/expression/common/Reason.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package expression.common;
|
||||||
|
|
||||||
|
import base.Either;
|
||||||
|
import java.util.function.LongUnaryOperator;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||||
|
*/
|
||||||
|
public final class Reason {
|
||||||
|
|
||||||
|
public static final Reason OVERFLOW = new Reason("Overflow");
|
||||||
|
public static final Reason DBZ = new Reason("Division by zero");
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
public Reason(final String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Either<Reason, T> eval(final Supplier<T> action) {
|
||||||
|
try {
|
||||||
|
return Either.right(action.get());
|
||||||
|
} catch (final ReasonException e) {
|
||||||
|
return Either.left(e.reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int overflow(final long value) {
|
||||||
|
return value < Integer.MIN_VALUE || Integer.MAX_VALUE < value
|
||||||
|
? OVERFLOW.error()
|
||||||
|
: (int) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T error() {
|
||||||
|
throw new ReasonException(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongUnaryOperator less(
|
||||||
|
final long limit,
|
||||||
|
final LongUnaryOperator op
|
||||||
|
) {
|
||||||
|
return a -> a < limit ? error() : op.applyAsLong(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongUnaryOperator greater(
|
||||||
|
final int limit,
|
||||||
|
final LongUnaryOperator op
|
||||||
|
) {
|
||||||
|
return a -> a > limit ? error() : op.applyAsLong(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ReasonException extends RuntimeException {
|
||||||
|
|
||||||
|
private final Reason reason;
|
||||||
|
|
||||||
|
public ReasonException(final Reason reason) {
|
||||||
|
super(reason.description);
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("Reason(%s)", description);
|
||||||
|
}
|
||||||
|
}
|
||||||
71
java/expression/common/Renderer.java
Normal file
71
java/expression/common/Renderer.java
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package expression.common;
|
||||||
|
|
||||||
|
import base.Functional;
|
||||||
|
import base.Pair;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||||
|
*/
|
||||||
|
public interface Renderer<C, S, R> {
|
||||||
|
static <C, S, R> Builder<C, S, R> builder(final Node.Const<C, R> constant) {
|
||||||
|
return new Builder<>(constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
R render(final Expr<C, R> expr, final S settings);
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface UnaryOperator<S, R> {
|
||||||
|
R apply(S settings, R arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface BinaryOperator<S, R> {
|
||||||
|
R apply(S settings, R arg1, R arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Builder<C, S, R> {
|
||||||
|
|
||||||
|
private final Node.Const<C, R> constant;
|
||||||
|
private final Map<String, UnaryOperator<S, R>> unary = new HashMap<>();
|
||||||
|
private final Map<String, BinaryOperator<S, R>> binary =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
|
private Builder(final Node.Const<C, R> constant) {
|
||||||
|
this.constant = constant;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unary(final String name, final UnaryOperator<S, R> op) {
|
||||||
|
unary.put(name, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void binary(final String name, final BinaryOperator<S, R> op) {
|
||||||
|
binary.put(name, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Renderer<C, S, R> build() {
|
||||||
|
return (expr, settings) -> {
|
||||||
|
final Map<String, R> vars = expr
|
||||||
|
.variables()
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toMap(Pair::first, Pair::second));
|
||||||
|
return expr
|
||||||
|
.node()
|
||||||
|
.cata(
|
||||||
|
constant,
|
||||||
|
name -> Functional.get(vars, name),
|
||||||
|
(name, p, arg) ->
|
||||||
|
Functional.get(unary, name).apply(settings, arg),
|
||||||
|
(name, arg1, arg2) ->
|
||||||
|
Functional.get(binary, name).apply(
|
||||||
|
settings,
|
||||||
|
arg1,
|
||||||
|
arg2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
java/expression/common/TestGenerator.java
Normal file
72
java/expression/common/TestGenerator.java
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package expression.common;
|
||||||
|
|
||||||
|
import base.Pair;
|
||||||
|
import base.TestCounter;
|
||||||
|
import expression.ToMiniString;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class TestGenerator<C, E extends ToMiniString> {
|
||||||
|
|
||||||
|
private final Generator<C, E> generator;
|
||||||
|
private final NodeRenderer<C> renderer;
|
||||||
|
|
||||||
|
public TestGenerator(
|
||||||
|
final Generator<C, E> generator,
|
||||||
|
final NodeRenderer<C> renderer
|
||||||
|
) {
|
||||||
|
this.generator = generator;
|
||||||
|
this.renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBasic(final Consumer<Test<C, E>> test) {
|
||||||
|
generator.testBasic(consumer(test));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRandom(
|
||||||
|
final TestCounter counter,
|
||||||
|
final int denominator,
|
||||||
|
final Consumer<Test<C, E>> test
|
||||||
|
) {
|
||||||
|
generator.testRandom(counter, denominator, consumer(test));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumer<Expr<C, E>> consumer(
|
||||||
|
final Consumer<TestGenerator.Test<C, E>> consumer
|
||||||
|
) {
|
||||||
|
return expr ->
|
||||||
|
consumer.accept(new TestGenerator.Test<>(expr, renderer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Pair<String, E>> variables(final int count) {
|
||||||
|
return generator.variables(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String render(
|
||||||
|
final Expr<C, ?> expr,
|
||||||
|
final NodeRenderer.Settings settings
|
||||||
|
) {
|
||||||
|
return renderer.render(expr, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Test<C, E> {
|
||||||
|
|
||||||
|
public final Expr<C, E> expr;
|
||||||
|
private final Map<NodeRenderer.Settings, String> rendered =
|
||||||
|
new HashMap<>();
|
||||||
|
private final NodeRenderer<C> renderer;
|
||||||
|
|
||||||
|
public Test(final Expr<C, E> expr, final NodeRenderer<C> renderer) {
|
||||||
|
this.expr = expr;
|
||||||
|
this.renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String render(final NodeRenderer.Settings settings) {
|
||||||
|
return rendered.computeIfAbsent(settings, s ->
|
||||||
|
renderer.render(expr, s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
java/expression/common/package-info.java
Normal file
7
java/expression/common/package-info.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Expressions generators for expression-based homeworks
|
||||||
|
* of <a href="https://www.kgeorgiy.info/courses/prog-intro/">Introduction to Programming</a> course.
|
||||||
|
*
|
||||||
|
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||||
|
*/
|
||||||
|
package expression.common;
|
||||||
Reference in New Issue
Block a user