package mhfc.net.common.util.parsing.syntax.tree; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.List; import java.util.Objects; import mhfc.net.common.util.BitSetIterator; import mhfc.net.common.util.parsing.syntax.ITerminalElement; public class UnarySyntaxBuilder { /* package */ class ValueRegistration { public final Class<?> clazz; public final int valueID; public final BitSet equivalents; public final BitSet parents; public ValueRegistration(Class<?> clazz, int id, int parentID) { this.clazz = Objects.requireNonNull(clazz); this.valueID = id; this.equivalents = new BitSet(); this.equivalents.set(id); this.parents = new BitSet(); if (parentID != -1) { ValueRegistration parentReg = values.get(parentID); if (!parentReg.clazz.isAssignableFrom(clazz)) { throw new IllegalArgumentException( "Subclass " + clazz + " must be assignable to parent class " + parentReg.clazz); } this.parents.set(parentID); this.parents.or(parentReg.parents); for (int i : BitSetIterator.asIndexIterable(parents)) { values.get(i).equivalents.set(valueID); } } } } public enum ElementType { VALUE, PREFIX_OP, POSTFIX_OP; } /* package */ class OperatorRegistration { public final Class<?> clazz; public final int operatorID; public final boolean isPrefix; public final ElementType resultType; public final int resultID; // a bit is set at pos p if this operator has a lower precedence that // the operator with id p // Same means prefix > prefix public final BitSet comesAfterSameFixity; // Other means prefix > postfix public final BitSet comesAfterOtherFixity; // Same means prefix > prefix public final BitSet comesBeforeSameFixity; // Other means prefix > postfix public final BitSet comesBeforeOtherFixity; // The bitset of the ValueRegistration this op has been registered for public final BitSet acceptedValues; public OperatorRegistration( Class<?> clazz, int id, boolean isPrefix, int valueID, ElementType type, int resultID) { this.clazz = Objects.requireNonNull(clazz); this.operatorID = id; this.isPrefix = isPrefix; this.comesAfterOtherFixity = new BitSet(); this.comesAfterSameFixity = new BitSet(); this.comesBeforeOtherFixity = new BitSet(); this.comesBeforeSameFixity = new BitSet(); this.resultType = Objects.requireNonNull(type); this.resultID = resultID; ValueRegistration reg = values.get(valueID); // No copy this.acceptedValues = reg.equivalents; } private List<OperatorRegistration> otherRegistry() { return isPrefix ? postfixOps : prefixOps; } private List<OperatorRegistration> sameRegistry() { return isPrefix ? prefixOps : postfixOps; } public void addAfter(int otherID) { OperatorRegistration other = otherRegistry().get(otherID); comesAfterOtherFixity.set(otherID); comesAfterOtherFixity.or(other.comesAfterSameFixity); comesAfterSameFixity.or(other.comesAfterOtherFixity); for (int idx : BitSetIterator.asIndexIterable(comesBeforeSameFixity)) { sameRegistry().get(idx).comesAfterOtherFixity.set(otherID); sameRegistry().get(idx).comesAfterOtherFixity.or(other.comesAfterSameFixity); sameRegistry().get(idx).comesAfterSameFixity.or(other.comesAfterOtherFixity); } for (int idx : BitSetIterator.asIndexIterable(comesBeforeOtherFixity)) { otherRegistry().get(idx).comesAfterSameFixity.set(otherID); otherRegistry().get(idx).comesAfterSameFixity.or(other.comesAfterSameFixity); otherRegistry().get(idx).comesAfterOtherFixity.or(other.comesAfterOtherFixity); } other.comesBeforeOtherFixity.set(operatorID); other.comesBeforeOtherFixity.or(comesBeforeSameFixity); other.comesBeforeSameFixity.or(comesBeforeOtherFixity); for (int idx : BitSetIterator.asIndexIterable(other.comesAfterSameFixity)) { otherRegistry().get(idx).comesBeforeOtherFixity.set(operatorID); otherRegistry().get(idx).comesBeforeOtherFixity.or(comesBeforeSameFixity); otherRegistry().get(idx).comesBeforeSameFixity.or(comesBeforeOtherFixity); } for (int idx : BitSetIterator.asIndexIterable(other.comesAfterOtherFixity)) { sameRegistry().get(idx).comesBeforeSameFixity.set(operatorID); sameRegistry().get(idx).comesBeforeSameFixity.or(comesBeforeSameFixity); sameRegistry().get(idx).comesBeforeOtherFixity.or(comesBeforeOtherFixity); } } } private List<ValueRegistration> values; private List<OperatorRegistration> prefixOps; private List<OperatorRegistration> postfixOps; private boolean validated; public UnarySyntaxBuilder() { values = new ArrayList<>(); prefixOps = new ArrayList<>(); postfixOps = new ArrayList<>(); validated = false; } /** * When something changes and has to be revalidated */ protected void invalidate() { validated = false; } /* package */ List<ValueRegistration> getValues() { return Collections.unmodifiableList(values); } /* package */ List<OperatorRegistration> getPrefixOps() { return Collections.unmodifiableList(prefixOps); } /* package */ List<OperatorRegistration> getPostfixOps() { return Collections.unmodifiableList(postfixOps); } /** * Registers clazz as a value. * * @param clazz * @return the id to use in {@link AST#pushValue(int, ITerminalElement)}. */ public int registerTerminal(Class<?> clazz) { invalidate(); int nextID = values.size(); values.add(new ValueRegistration(clazz, nextID, -1)); return nextID; } /** * Registers a clazz that can be a substitute for the parentID, like a child-class.<br> * The class of parentID must be a superclass of clazz. * * @param clazz * @param parentID * @return */ public int registerTerminal(Class<?> clazz, int parentID) { invalidate(); int nextID = values.size(); values.add(new ValueRegistration(clazz, nextID, parentID)); return nextID; } protected OperatorRegistration registerOperator( Class<?> classOP, boolean isPrefix, int valueID, ElementType resultType, int resultID) { invalidate(); List<OperatorRegistration> opReg = isPrefix ? prefixOps : postfixOps; int nextID = opReg.size(); OperatorRegistration reg = new OperatorRegistration(classOP, nextID, isPrefix, valueID, resultType, resultID); opReg.add(reg); return reg; } /** * Declares that prefix binds stronger than postfix if forPre is true. * * @param opId1 * @param opId2 */ protected void declarePrecedence(int prefix, int postfix, boolean forPre) { invalidate(); if (forPre) { postfixOps.get(postfix).addAfter(prefix); } else { prefixOps.get(prefix).addAfter(postfix); } } protected void revalidate() { List<SyntaxBuilder.OperatorRegistration> preOpReg = getPrefixOps(); List<SyntaxBuilder.OperatorRegistration> postOpReg = getPostfixOps(); for (UnarySyntaxBuilder.OperatorRegistration opReg : preOpReg) { if (opReg.comesAfterSameFixity.get(opReg.operatorID) || opReg.comesBeforeSameFixity.get(opReg.operatorID)) { throw new IllegalStateException("Circular precedence"); } } for (UnarySyntaxBuilder.OperatorRegistration opReg : postOpReg) { if (opReg.comesAfterSameFixity.get(opReg.operatorID) || opReg.comesBeforeSameFixity.get(opReg.operatorID)) { throw new IllegalStateException("Circular precedence"); } } for (UnarySyntaxBuilder.OperatorRegistration preOp : preOpReg) { for (UnarySyntaxBuilder.OperatorRegistration postOp : postOpReg) { if (!preOp.acceptedValues.intersects(postOp.acceptedValues)) { continue; } boolean preOpAfter = preOp.comesAfterOtherFixity.get(postOp.operatorID); boolean postOpAfter = postOp.comesAfterOtherFixity.get(preOp.operatorID); if (preOpAfter == postOpAfter) { throw new IllegalStateException( "Some operators are not ordered: " + preOp.operatorID + " " + postOp.operatorID); } } } } public UnarySyntaxBuilder validate() { if (validated) { return this; } revalidate(); validated = true; return this; } }