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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user