package bjc.dicelang.ast; import java.util.function.BinaryOperator; import bjc.utils.data.IPair; import bjc.utils.data.Pair; import bjc.utils.funcdata.IList; import bjc.utils.funcdata.ITree; import bjc.utils.funcdata.Tree; import bjc.dicelang.ast.nodes.IDiceASTNode; import bjc.dicelang.ast.nodes.OperatorDiceNode; /** * Responsible for collapsing arithmetic operators * * @author ben * */ final class ArithmeticCollapser implements IOperatorCollapser { private OperatorDiceNode type; private BinaryOperator valueOp; public ArithmeticCollapser(OperatorDiceNode type, BinaryOperator valueOp) { this.type = type; this.valueOp = valueOp; } @Override public IPair> apply( IList>> nodes) { IPair> initState = new Pair<>( new IntegerResult(0), new Tree<>(type)); BinaryOperator>> reducer = ( currentState, accumulatedState) -> { // Force evaluation of accumulated state to prevent // certain bugs from occuring accumulatedState.merge((l, r) -> null); return reduceStates(accumulatedState, currentState); }; IPair> reducedState = nodes .reduceAux(initState, reducer, (state) -> state); return reducedState; } private IList combineArrayResults(IResult accumulatedValue, IResult currentValue) { IList currentList = ((ArrayResult) currentValue) .getValue(); IList accumulatedList = ((ArrayResult) accumulatedValue) .getValue(); if (currentList.getSize() != accumulatedList.getSize()) { throw new UnsupportedOperationException( "Can only apply operations to equal-length arrays"); } IList resultList = currentList.combineWith( accumulatedList, (currentNode, accumulatedNode) -> { boolean currentNotInt = currentNode .getType() != ResultType.INTEGER; boolean accumulatedNotInt = accumulatedNode .getType() != ResultType.INTEGER; if (currentNotInt || accumulatedNotInt) { throw new UnsupportedOperationException( "Nesting of array operations isn't allowed"); } int accumulatedInt = ((IntegerResult) accumulatedNode) .getValue(); int currentInt = ((IntegerResult) currentNode) .getValue(); IResult combinedValue = new IntegerResult( valueOp.apply(accumulatedInt, currentInt)); return combinedValue; }); return resultList; } private IPair> doArithmeticCollapse( IResult accumulatedValue, ITree accumulatedTree, IResult currentValue) { boolean currentIsInt = currentValue .getType() == ResultType.INTEGER; boolean accumulatedIsInt = accumulatedValue .getType() == ResultType.INTEGER; if (!currentIsInt) { if (!accumulatedIsInt) { IList resultList = combineArrayResults( accumulatedValue, currentValue); return new Pair<>(new ArrayResult(resultList), accumulatedTree); } IList resultList = halfCombineLists( ((ArrayResult) currentValue).getValue(), accumulatedValue, true); return new Pair<>(new ArrayResult(resultList), accumulatedTree); } else if (!accumulatedIsInt) { IList resultList = halfCombineLists( ((ArrayResult) accumulatedValue).getValue(), currentValue, false); return new Pair<>(new ArrayResult(resultList), accumulatedTree); } int accumulatedInt = ((IntegerResult) accumulatedValue).getValue(); int currentInt = ((IntegerResult) currentValue).getValue(); int combinedValue = valueOp.apply(accumulatedInt, currentInt); return new Pair<>(new IntegerResult(combinedValue), accumulatedTree); } private IList halfCombineLists(IList list, IResult scalar, boolean scalarLeft) { if (scalar.getType() != ResultType.INTEGER) { throw new UnsupportedOperationException( "Nested array operations not supported"); } int scalarInt = ((IntegerResult) scalar).getValue(); return list.map((element) -> { if (element.getType() != ResultType.INTEGER) { throw new UnsupportedOperationException( "Nested array operations not supported"); } int elementInt = ((IntegerResult) element).getValue(); IResult combinedValue; if (scalarLeft) { combinedValue = new IntegerResult( valueOp.apply(scalarInt, elementInt)); } else { combinedValue = new IntegerResult( valueOp.apply(elementInt, scalarInt)); } return combinedValue; }); } private IPair> reduceStates( IPair> accumulatedState, IPair> currentState) { return accumulatedState .bind((accumulatedValue, accumulatedTree) -> { return currentState .bind((currentValue, currentTree) -> { accumulatedTree.addChild(currentTree); return doArithmeticCollapse( accumulatedValue, accumulatedTree, currentValue); }); }); } }