package mhfc.net.common.util.parsing.syntax.tree;
import java.util.List;
import java.util.Objects;
import java.util.Stack;
import mhfc.net.common.util.parsing.syntax.operators.IOperator;
import net.minecraft.command.SyntaxErrorException;
public class UnaryAST {
private static abstract class Node {
/**
* Add in depth first nature. Return true if no more upward nodes should be added.
*
* @param stack
* @return
*/
public boolean addDisputableNodes(Stack<IntermediateNode> stack) {
return false;
}
public void addOpenPostfixOps(Stack<IntermediateNode> stack) {}
public void addOpenPrefixOps(Stack<IntermediateNode> stack) {}
}
private static interface IValueNode {
int getValueType();
Object getVal();
}
private static interface IOperatorNode {
boolean isPrefixOP();
int getOpType();
IOperator<?, ?> getOp();
}
/**
* A value provided directly by the parser.
*/
private static class ValueLeaf extends Node implements IValueNode {
private final int valueType;
private final Object value;
public ValueLeaf(int valueType, Object value) {
this.valueType = valueType;
this.value = value;
}
@Override
public int getValueType() {
return valueType;
}
@Override
public Object getVal() {
return value;
}
@Override
public String toString() {
return Objects.toString(value);
}
}
/**
* An operator provided directly by the parser.
*/
private static class OperatorLeaf extends Node implements IOperatorNode {
private final int opType;
private final boolean isPrefix;
private final IOperator<?, ?> op;
public OperatorLeaf(int opType, boolean isPrefix, IOperator<?, ?> op) {
this.opType = opType;
this.isPrefix = isPrefix;
this.op = op;
}
@Override
public boolean isPrefixOP() {
return isPrefix;
}
@Override
public int getOpType() {
return opType;
}
@Override
public IOperator<?, ?> getOp() {
return op;
}
@Override
public String toString() {
return Objects.toString(op);
}
}
private static abstract class IntermediateNode extends Node {
private Node valueNode;
private IValueNode valueNodeAsVal;
private final Node opNode;
private final IOperatorNode opNodeAsOp;
public <T extends Node & IOperatorNode> IntermediateNode(T opNode) {
this(opNode, null);
}
public <T extends Node & IOperatorNode, Q extends Node & IValueNode> IntermediateNode(T opNode, Q valNode) {
this.opNode = Objects.requireNonNull(opNode);
this.opNodeAsOp = opNode;
setValueNode(valNode);
}
public Node getValueNode() {
return valueNode;
}
public IValueNode getValue() {
return valueNodeAsVal;
}
public <T extends Node & IValueNode> void setValueNode(T newNode) {
this.valueNode = newNode;
this.valueNodeAsVal = newNode;
}
public Node getOperatorNode() {
return opNode;
}
public IOperatorNode getOperator() {
return opNodeAsOp;
}
private boolean isPrefix() {
return opNodeAsOp.isPrefixOP();
}
public Object compute() {
if (this.valueNode == null) {
throw new IllegalStateException("Unfinished tree");
}
return compute(this.opNodeAsOp.getOp(), this.valueNodeAsVal.getVal());
}
@SuppressWarnings("unchecked")
private <T> Object compute(IOperator<T, ?> op, Object object) {
return op.with((T) object);
}
/**
* Return the right most non-empty child node
*
* @return
*/
private Node getRightMostNode() {
return isPrefix() ? getValue() != null ? getValueNode() : getOperatorNode() : getOperatorNode();
}
/**
* Return the left most non-empty child node
*
* @return
*/
private Node getLeftMostNode() {
return isPrefix() ? getOperatorNode() : getValue() != null ? getValueNode() : getOperatorNode();
}
@Override
public boolean addDisputableNodes(Stack<IntermediateNode> stack) {
if (getValue() == null) {
if (isPrefix()) {
getOperatorNode().addDisputableNodes(stack);
}
// Cancel when an incomplete op is encountered
return true;
}
// Traverse down the right path
boolean childrenResult = getRightMostNode().addDisputableNodes(stack);
if (!childrenResult && isPrefix()) {
// Add to the bottom of the stack
stack.add(0, this);
}
return childrenResult;
}
@Override
public void addOpenPostfixOps(Stack<IntermediateNode> stack) {
if (!isPrefix() && getValue() == null) {
stack.add(this);
}
getLeftMostNode().addOpenPostfixOps(stack);
}
@Override
public void addOpenPrefixOps(Stack<IntermediateNode> stack) {
if (isPrefix() && getValue() == null) {
stack.add(this);
}
getRightMostNode().addOpenPrefixOps(stack);
}
}
/**
* A value that is the result of an operator application
*/
private static class ValueIntermediate extends IntermediateNode implements IValueNode {
private int resultType;
public <T extends Node & IOperatorNode> ValueIntermediate(T opNode, int resultType) {
super(opNode);
this.resultType = resultType;
}
@Override
public int getValueType() {
return resultType;
}
@Override
public Object getVal() {
return compute();
}
@Override
public String toString() {
return getOperator() + "(" + getValue() + ")";
}
}
/**
* An operator that is the result of an operator application.
*/
private static class OperatorIntermediate extends IntermediateNode implements IOperatorNode {
private final int opType;
private final boolean isPrefix;
public <T extends Node & IOperatorNode> OperatorIntermediate(T opNode, boolean isPrefix, int resultOpType) {
super(opNode);
this.isPrefix = isPrefix;
this.opType = resultOpType;
}
@Override
public boolean isPrefixOP() {
return isPrefix;
}
@Override
public int getOpType() {
return opType;
}
@Override
public IOperator<?, ?> getOp() {
return (IOperator<?, ?>) compute();
}
@Override
public String toString() {
return getOperator() + "(" + getValue() + ")";
}
}
private class Tree {
// Doesn't include the top of the tree
private Stack<IntermediateNode> depthFirstDisputableValues;
private Stack<IntermediateNode> depthFirstOpenPostfixOps;
private Stack<IntermediateNode> depthFirstOpenPrefixOps;
private Node topNode;
private IValueNode topNodeAsVal;
public Tree(boolean isPrefix, int opID, IOperator<?, ?> op) {
this(makeNodesFor(isPrefix, opID, op));
}
public Tree(int valueID, Object value) {
this(new ValueLeaf(valueID, value));
}
private <T extends Node & IValueNode> Tree(T topNode) {
this.topNode = topNode;
this.topNodeAsVal = topNode;
depthFirstDisputableValues = new Stack<>();
depthFirstOpenPostfixOps = new Stack<>();
depthFirstOpenPrefixOps = new Stack<>();
reparseDisputableValues();
reparsePostfixOps();
reparsePrefixOps();
}
private void reparseDisputableValues() {
this.depthFirstDisputableValues.clear();
this.topNode.addDisputableNodes(depthFirstDisputableValues);
}
private void reparsePostfixOps() {
this.depthFirstOpenPostfixOps.clear();
this.topNode.addOpenPostfixOps(depthFirstOpenPostfixOps);
}
private void reparsePrefixOps() {
this.depthFirstOpenPrefixOps.clear();
this.topNode.addOpenPrefixOps(depthFirstOpenPrefixOps);
}
private boolean tryApplyTreeTo(IntermediateNode target) {
// Merge o
if (!accepts(target, topNodeAsVal)) {
return false;
}
target.setValueNode((Node & IValueNode) topNode);
topNode = null;
topNodeAsVal = null;
return true;
}
private boolean tryOvertakeValueNode(IntermediateNode disputable) {
if (depthFirstOpenPostfixOps.isEmpty()) {
return false;
}
IntermediateNode openOP = depthFirstOpenPostfixOps.peek();
if (!accepts(openOP, disputable.getValue())) {
return false;
}
if (isPrefixPrefered(disputable, openOP)) {
return false;
}
openOP.setValueNode((Node & IValueNode) disputable.getValue());
disputable.setValueNode(null);
depthFirstOpenPostfixOps.pop();
return true;
}
private boolean tryOvertakeWholeTree(Tree other) {
if (depthFirstOpenPostfixOps.empty()) {
return false;
}
IntermediateNode openOP = depthFirstOpenPostfixOps.peek();
if (!accepts(openOP, other.topNodeAsVal)) {
return false;
}
openOP.setValueNode((Node & IValueNode) other.topNode);
other.topNode = null;
other.topNodeAsVal = null;
depthFirstOpenPostfixOps.pop();
return true;
}
/**
* Will merge left <- this. Returns true if something changed. Leaves the tree with topNode == null iff the
* merge is complete. Aka, one of them should be deleted.
*
* @param left
* @return
*/
public boolean mergeInto(Tree left) throws SyntaxErrorException {
if (depthFirstOpenPostfixOps.isEmpty()) {
// Merge our value
if (left.depthFirstOpenPrefixOps.isEmpty()) {
// Both trees represent values
return false;
}
IntermediateNode openOP = left.depthFirstOpenPrefixOps.pop();
if (tryApplyTreeTo(openOP)) {
left.reparseDisputableValues();
left.reparsePrefixOps();
return true;
}
}
while (!left.depthFirstDisputableValues.empty()) {
IntermediateNode node = left.depthFirstDisputableValues.pop();
if (tryOvertakeValueNode(node)) {
left.reparseDisputableValues();
left.reparsePrefixOps();
this.reparseDisputableValues();
this.reparsePostfixOps();
return true;
}
}
// Try to swallow left's top node
if (tryOvertakeWholeTree(left)) {
this.reparseDisputableValues();
this.reparsePostfixOps();
this.reparsePrefixOps();
return true;
}
throw new SyntaxErrorException("Impossible sequence encountered " + left + " " + this);
}
@Override
public String toString() {
return "Tree { top: " + this.topNode + ", open: " + this.depthFirstOpenPrefixOps + "|"
+ depthFirstOpenPostfixOps + ", disputable: " + this.depthFirstDisputableValues + "}";
}
}
private static interface NodeFunction {
<T extends Node & IOperatorNode> ValueIntermediate make(T op);
}
// map prefix_id, postfix_id -> boolean, true if prefix has higher fixity
// final
private final boolean[][] isPrefixFixityHigher;
// map prefix_id, value_id -> boolean, true if applicable
// final
private final boolean[][] isPrefixApplicable;
// map postfix_id, value_id -> boolean, true if applicable
// final
private final boolean[][] isPostfixApplicable;
// map prefix_id -> Supplier
private final NodeFunction[] prefixOpTreeBuilder;
private final NodeFunction[] postfixOPTreeBuilder;
private Stack<Tree> partialTrees;
/* package */ UnaryAST(UnarySyntaxBuilder builder) {
builder.validate();
List<SyntaxBuilder.OperatorRegistration> preOpReg = builder.getPrefixOps();
List<SyntaxBuilder.OperatorRegistration> postOpReg = builder.getPostfixOps();
List<SyntaxBuilder.ValueRegistration> valueReg = builder.getValues();
isPrefixFixityHigher = new boolean[preOpReg.size()][postOpReg.size()];
isPrefixApplicable = new boolean[preOpReg.size()][valueReg.size()];
isPostfixApplicable = new boolean[postOpReg.size()][valueReg.size()];
prefixOpTreeBuilder = new NodeFunction[preOpReg.size()];
postfixOPTreeBuilder = new NodeFunction[postOpReg.size()];
for (UnarySyntaxBuilder.OperatorRegistration preOp : preOpReg) {
for (int i = 0; i < postOpReg.size(); i++) {
boolean preOpAfter = preOp.comesAfterOtherFixity.get(i);
isPrefixFixityHigher[preOp.operatorID][i] = !preOpAfter;
}
for (int i = 0; i < valueReg.size(); i++) {
isPrefixApplicable[preOp.operatorID][i] = preOp.acceptedValues.get(i);
}
prefixOpTreeBuilder[preOp.operatorID] = makeNodeGenerator(preOp);
}
for (UnarySyntaxBuilder.OperatorRegistration postOp : postOpReg) {
for (int i = 0; i < valueReg.size(); i++) {
isPostfixApplicable[postOp.operatorID][i] = postOp.acceptedValues.get(i);
}
postfixOPTreeBuilder[postOp.operatorID] = makeNodeGenerator(postOp);
}
partialTrees = new Stack<>();
}
/**
* Clones the parse structure, but *not* the current state of the AST, i.e. all operators and precedence
*
* @param structure
*/
protected UnaryAST(UnaryAST structure) {
this.isPrefixFixityHigher = structure.isPrefixFixityHigher;
this.isPrefixApplicable = structure.isPrefixApplicable;
this.isPostfixApplicable = structure.isPostfixApplicable;
this.prefixOpTreeBuilder = structure.prefixOpTreeBuilder;
this.postfixOPTreeBuilder = structure.postfixOPTreeBuilder;
this.partialTrees = new Stack<>();
}
private NodeFunction makeNodeGenerator(SyntaxBuilder.OperatorRegistration operator) {
switch (operator.resultType) {
case VALUE:
return new NodeFunction() {
@Override
public <T extends Node & IOperatorNode> ValueIntermediate make(T op) {
return new ValueIntermediate(op, operator.resultID);
}
};
case PREFIX_OP:
return new NodeFunction() {
@Override
public <T extends Node & IOperatorNode> ValueIntermediate make(T op) {
return prefixOpTreeBuilder[operator.resultID]
.make(new OperatorIntermediate(op, true, operator.resultID));
}
};
case POSTFIX_OP:
return new NodeFunction() {
@Override
public <T extends Node & IOperatorNode> ValueIntermediate make(T op) {
return postfixOPTreeBuilder[operator.resultID]
.make(new OperatorIntermediate(op, false, operator.resultID));
}
};
}
throw new IllegalArgumentException("is resultType null?");
}
public Object getOverallValue() throws SyntaxErrorException {
if (partialTrees.size() != 1) {
throw new SyntaxErrorException("Not exactly a single tree remaining: " + partialTrees);
}
try {
Tree tree = partialTrees.peek();
return tree.topNodeAsVal.getVal();
} catch (IllegalStateException ise) {
throw new SyntaxErrorException("Unfinished operator" + partialTrees);
}
}
/**
*
* @param valueId
* @param val
* @throws SyntaxErrorException
* if the tree can't be fulfilled. For example when the first thing getting pushed is a postfix operator
*/
protected void pushValue(int valueId, Object val) throws SyntaxErrorException {
// valueClasses[valueId].cast(val);
partialTrees.push(new Tree(valueId, val));
remergeTrees();
}
protected void pushOperator(boolean isPrefix, int opId, IOperator<?, ?> op) throws SyntaxErrorException {
// (isPrefix ? prefixClasses : postfixClasses)[opId].cast(op);
Objects.requireNonNull(op);
partialTrees.push(new Tree(isPrefix, opId, op));
remergeTrees();
}
private void remergeTrees() {
boolean somethingHappened = true;
while (somethingHappened && partialTrees.size() >= 2) {
Tree one = partialTrees.pop();
Tree two = partialTrees.pop();
somethingHappened = one.mergeInto(two);
if (two.topNode != null) {
partialTrees.push(two);
}
if (one.topNode != null) {
partialTrees.push(one);
}
}
}
private boolean accepts(IntermediateNode op, IValueNode valueType) {
if (op.isPrefix()) {
return isPrefixApplicable[op.getOperator().getOpType()][valueType.getValueType()];
}
return isPostfixApplicable[op.getOperator().getOpType()][valueType.getValueType()];
}
private boolean isPrefixPrefered(IntermediateNode prefixOP, IntermediateNode postfixOP) {
if (!prefixOP.isPrefix() || postfixOP.isPrefix()) {
throw new IllegalArgumentException(
"Messed up! Two operators of the same type are fighting for a value" + prefixOP + postfixOP);
}
return isPrefixFixityHigher[prefixOP.getOperator().getOpType()][postfixOP.getOperator().getOpType()];
}
private ValueIntermediate makeNodesFor(boolean isPrefix, int opID, IOperator<?, ?> op) {
return (isPrefix ? prefixOpTreeBuilder : postfixOPTreeBuilder)[opID].make(new OperatorLeaf(opID, isPrefix, op));
}
}