All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.janno.evaluator.dice.Operator Maven / Gradle / Ivy

The newest version!
package de.janno.evaluator.dice;

import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;

import java.util.List;
import java.util.Optional;

@EqualsAndHashCode
@ToString
public abstract class Operator {
    protected final int maxNumberOfElements;
    protected final boolean keepChildrenRolls;
    @NonNull
    private final String name;
    private final Integer unaryPrecedence;
    private final Integer binaryPrecedence;

    private final Associativity unaryAssociativity;
    private final Associativity binaryAssociativity;

    public Operator(@NonNull String name, OperatorType operatorType, @NonNull Associativity associativity, int precedence, int maxNumberOfElements, boolean keepChildrenRolls) {
        this(name, getUnaryAssociativity(operatorType, associativity),
                getUnaryPrecedence(operatorType, precedence),
                getBinaryAssociativity(operatorType, associativity),
                getBinaryPrecedence(operatorType, precedence),
                maxNumberOfElements,
                keepChildrenRolls);
    }

    /**
     * The precedence is the priority of the operator.
     * An operator with a higher precedence will be executed before an operator with a lower precedence.
     * Example : In "1+3*4" * has a higher precedence than +, so the expression is interpreted as 1+(3*4).
     * An operator's associativity define how operators of the same precedence are grouped.
     */
    public Operator(@NonNull String name, Associativity unaryAssociativity, Integer unaryPrecedence, Associativity binaryAssociativity, Integer binaryPrecedence, int maxNumberOfElements, boolean keepChildrenRolls) {
        if (unaryAssociativity == null && binaryAssociativity == null) {
            throw new IllegalArgumentException("The operant %s need at least on associativity".formatted(name));
        }
        this.name = name;
        this.unaryAssociativity = unaryAssociativity;
        this.binaryAssociativity = binaryAssociativity;
        this.unaryPrecedence = unaryPrecedence;
        this.binaryPrecedence = binaryPrecedence;
        this.maxNumberOfElements = maxNumberOfElements;
        this.keepChildrenRolls = keepChildrenRolls;
    }

    private static Associativity getUnaryAssociativity(OperatorType operatorType, @NonNull Associativity associativity) {
        if (operatorType == OperatorType.UNARY) {
            return associativity;
        }
        return null;
    }

    private static Associativity getBinaryAssociativity(OperatorType operatorType, @NonNull Associativity associativity) {
        if (operatorType == OperatorType.BINARY) {
            return associativity;
        }
        return null;
    }

    private static Integer getUnaryPrecedence(OperatorType operatorType, int precedence) {
        if (operatorType == OperatorType.UNARY) {
            return precedence;
        }
        return null;
    }

    private static Integer getBinaryPrecedence(OperatorType operatorType, int precedence) {
        if (operatorType == OperatorType.BINARY) {
            return precedence;
        }
        return null;
    }

    private static  Optional getIndexIfExists(List list, int index) {
        if (list.size() <= index) {
            return Optional.empty();
        }
        return Optional.ofNullable(list.get(index));
    }

    protected static String getBinaryOperatorExpression(ExpressionPosition expressionPosition, List operands) {
        String left = getIndexIfExists(operands, 0).map(RollBuilder::toExpression).orElse("");
        String right = getIndexIfExists(operands, 1).map(RollBuilder::toExpression).orElse("");
        return String.format("%s%s%s", left, expressionPosition.toStringWithExtension(), right);
    }

    protected static String getLeftUnaryExpression(ExpressionPosition expressionPosition, List operands) {
        String left = getIndexIfExists(operands, 0).map(RollBuilder::toExpression).orElse("");
        return String.format("%s%s", left, expressionPosition.toStringWithExtension());
    }

    protected static String getRightUnaryExpression(ExpressionPosition expressionPosition, List operands) {
        String right = getIndexIfExists(operands, 0).map(RollBuilder::toExpression).orElse("");
        return String.format("%s%s", expressionPosition.toStringWithExtension(), right);
    }

    /**
     * Creates a RollBuilder for the arguments
     *
     * @param operands           1 or 2 operants
     * @param expressionPosition the part of the expression for this operant, is needed the get the used upper/lower case in the result expression
     * @return the RollBuilder that can be called to get result rolls
     */
    public abstract @NonNull RollBuilder evaluate(@NonNull List operands, @NonNull ExpressionPosition expressionPosition) throws ExpressionException;

    public boolean supportUnaryOperation() {
        return unaryAssociativity != null;
    }

    public boolean supportBinaryOperation() {
        return binaryAssociativity != null;
    }

    public @NonNull String getName() {
        return name;
    }

    public Associativity getAssociativityForOperantType(OperatorType operatorType) {
        if (operatorType == OperatorType.UNARY && unaryAssociativity != null) {
            return unaryAssociativity;
        }
        if (operatorType == OperatorType.BINARY && binaryAssociativity != null) {
            return binaryAssociativity;
        }
        return null;
    }

    public int getPrecedenceForOperantType(OperatorType operatorType) {
        if (operatorType == OperatorType.UNARY && unaryPrecedence != null) {
            return unaryPrecedence;
        }
        if (operatorType == OperatorType.BINARY && binaryPrecedence != null) {
            return binaryPrecedence;
        }
        throw new IllegalStateException("'%s' has no precedence for a operand type of %s".formatted(getName(), operatorType));
    }

    public enum Associativity {
        LEFT,
        RIGHT
    }

    public enum OperatorType {
        UNARY(1),
        BINARY(2);

        public final int argumentCount;

        OperatorType(int argumentCount) {
            this.argumentCount = argumentCount;
        }

        public static OperatorType of(int argumentCount) {
            if (argumentCount == 1) {
                return UNARY;
            }
            if (argumentCount == 2) {
                return BINARY;
            }
            throw new IllegalArgumentException("OperatorType must have a argumentCount of 1 or 2");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy