package mhfc.net.common.util.parsing.syntax.tree; import java.util.ArrayList; import java.util.Collections; import java.util.List; import mhfc.net.common.util.parsing.syntax.operators.BinaryOPWrapper; import mhfc.net.common.util.parsing.syntax.operators.BinaryOPWrapper.BinaryWrapperResult; import mhfc.net.common.util.parsing.syntax.operators.IBinaryOperator; import mhfc.net.common.util.parsing.syntax.operators.IOperator; public class SyntaxBuilder extends UnarySyntaxBuilder { private enum PublicOpType { UNARY_PREFIX, UNARY_POSTFIX, BINARY; } /* package */ class OperatorRemap { public final int publicID; public final PublicOpType type; public final Class<?> clazz; private final int prefixID; // Only used for binary private final int postfixID; public OperatorRemap(boolean isPrefix, int publicID, int realID, Class<?> clazz) { this.type = isPrefix ? PublicOpType.UNARY_PREFIX : PublicOpType.UNARY_POSTFIX; this.publicID = publicID; this.prefixID = isPrefix ? realID : -1; this.postfixID = isPrefix ? -1 : realID; this.clazz = clazz; } public OperatorRemap(int publicID, int realIDPre, int realIDPost, Class<?> clazz) { this.type = PublicOpType.BINARY; this.publicID = publicID; this.prefixID = realIDPre; this.postfixID = realIDPost; this.clazz = clazz; } public boolean isDominantPrefix() { return type == PublicOpType.UNARY_PREFIX; } public int getDominantID() { return isDominantPrefix() ? prefixID : postfixID; } public int getPrefixID() { return prefixID; } public int getPostfixID() { return postfixID; } } private List<OperatorRemap> remapPublicIDs; public SyntaxBuilder() { super(); remapPublicIDs = new ArrayList<>(); } /* package */ List<OperatorRemap> getRemapPublicIDs() { return Collections.unmodifiableList(remapPublicIDs); } public <O extends IOperator<?, ?>> int registerUnaryOperator( Class<O> classOP, boolean isPrefix, int valueID, int resultID) { return registerUnaryOperator(classOP, isPrefix, valueID, ElementType.VALUE, resultID); } /** * Registers a unary operator. Note that no checks regarding the types of R and V is done. * * @param classOP * @param isPrefix * @param valueID * argument's value id * @param resultID * result's value id * @return */ public <O extends IOperator<?, ?>> int registerUnaryOperator( Class<O> classOP, boolean isPrefix, int valueID, ElementType type, int resultID) { if (type != ElementType.VALUE) { OperatorRemap remap = remapPublicIDs.get(resultID); resultID = remap.getDominantID(); } int internal = registerOperator(classOP, isPrefix, valueID, type, resultID).operatorID; int external = remapPublicIDs.size(); remapPublicIDs.add(new OperatorRemap(isPrefix, external, internal, classOP)); return external; } public <O extends IBinaryOperator<?, ?, ?>> int registerBinaryOperator( Class<O> classOP, int valueLeftID, int valueRightID, int resultID, boolean isLeftAssociative) { OperatorRegistration intermediate = registerOperator( BinaryWrapperResult.class, true, valueRightID, ElementType.VALUE, resultID); OperatorRegistration initial = registerOperator( BinaryOPWrapper.class, false, valueLeftID, ElementType.PREFIX_OP, intermediate.operatorID); declarePrecedence(intermediate.operatorID, initial.operatorID, isLeftAssociative); int externalID = remapPublicIDs.size(); remapPublicIDs.add(new OperatorRemap(externalID, intermediate.operatorID, initial.operatorID, classOP)); return externalID; } /** * Declares precedence of op1 over op2 * * @param opID1 * @param opID2 */ public void declarePrecedence(int opID1, int opID2) { OperatorRemap op1 = remapPublicIDs.get(opID1); OperatorRemap op2 = remapPublicIDs.get(opID2); int prefix1 = op1.getPrefixID(); int prefix2 = op2.getPrefixID(); int postfix1 = op1.getPostfixID(); int postfix2 = op2.getPostfixID(); if (prefix1 != -1 && postfix2 != -1) { declarePrecedence(prefix1, postfix2, true); } if (prefix2 != -1 && postfix1 != -1) { declarePrecedence(prefix2, postfix1, false); } } /** * Declares precedence of binary operators in cases of e.g. "5 + 6 - 4". leftToRight means this evaluates as * "(5 + 6) - 4", leftToRight false means this is evaluated as "5 + (6 - 4)" * * @param binaryOpID1 * @param binaryOpID2 * @param leftToRight */ public void declareSamePrecedence(int binaryOpID1, int binaryOpID2, boolean leftToRight) { OperatorRemap op1 = remapPublicIDs.get(binaryOpID1); OperatorRemap op2 = remapPublicIDs.get(binaryOpID2); int prefix1 = op1.getPrefixID(); int prefix2 = op2.getPrefixID(); int postfix1 = op1.getPostfixID(); int postfix2 = op2.getPostfixID(); if (prefix1 == -1 || postfix2 == -1 || prefix2 == -1 || postfix1 == -1) { throw new IllegalArgumentException("Both ops must be binary operators"); } declarePrecedence(prefix1, postfix2, leftToRight); declarePrecedence(prefix2, postfix1, leftToRight); } @Override public SyntaxBuilder validate() { super.validate(); return this; } public AST newParseTree() { return new AST(this); } }