package bjc.utils.parserutils; import java.util.Deque; import java.util.LinkedList; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import bjc.utils.data.GenHolder; import bjc.utils.data.IPair; import bjc.utils.data.Pair; import bjc.utils.funcdata.IFunctionalList; /** * Creates a parse tree from a postfix expression * * @author ben * */ public class TreeConstructor { private static final class TokenTransformer implements Consumer { private final class OperatorHandler implements Function>, AST>, IPair>, AST>> { private T element; public OperatorHandler(T element) { this.element = element; } @Override public IPair>, AST> apply(IPair>, AST> pair) { Deque> queuedASTs = pair.merge((queue, currentAST) -> queue); AST mergedAST = pair.merge((queue, currentAST) -> { AST newAST; if (isSpecialOperator.test(element)) { newAST = handleSpecialOperator.apply(queue); } else { if (queue.size() < 2) { throw new IllegalStateException( "Attempted to parse binary operator without enough operands.\n" + "Problem operator is " + element + "\nPossible operand is: \n\t" + queue.peek()); } AST rightAST = queue.pop(); AST leftAST = queue.pop(); newAST = new AST<>(element, leftAST, rightAST); } queue.push(newAST); return newAST; }); Pair>, AST> newPair = new Pair<>(queuedASTs, mergedAST); return newPair; } } private GenHolder>, AST>> initialState; private Predicate operatorPredicate; private Predicate isSpecialOperator; private Function>, AST> handleSpecialOperator; public TokenTransformer( GenHolder>, AST>> initialState, Predicate operatorPredicate, Predicate isSpecialOperator, Function>, AST> handleSpecialOperator) { this.initialState = initialState; this.operatorPredicate = operatorPredicate; this.isSpecialOperator = isSpecialOperator; this.handleSpecialOperator = handleSpecialOperator; } @Override public void accept(T element) { if (operatorPredicate.test(element)) { initialState.transform(new OperatorHandler(element)); } else { AST newAST = new AST<>(element); initialState.doWith((pair) -> { pair.doWith((queue, currentAST) -> { queue.push(newAST); }); }); initialState.transform((pair) -> { return pair.apply((Deque> queue) -> { return queue; }, (AST currentAST) -> { return newAST; }); }); } } } /** * Construct a tree from a list of tokens in postfix notation * * Only binary operators are accepted. * * @param * The elements of the parse tree * @param tokens * The list of tokens to build a tree from * @param operatorPredicate * The predicate to use to determine if something is a * operator * @return A AST from the expression */ public static AST constructTree(IFunctionalList tokens, Predicate operatorPredicate) { return constructTree(tokens, operatorPredicate, (op) -> false, null); } /** * Construct a tree from a list of tokens in postfix notation * * Only binary operators are accepted by default. Use the last two * parameters to handle non-binary operators * * @param * The elements of the parse tree * @param tokens * The list of tokens to build a tree from * @param operatorPredicate * The predicate to use to determine if something is a * operator * @param isSpecialOperator * The predicate to use to determine if an operator needs * special handling * @param handleSpecialOperator * The function to use to handle special case operators * @return A AST from the expression * * FIXME The handleSpecialOp function seems like an ugly * interface. Maybe there's a better way to express how that * works */ public static AST constructTree(IFunctionalList tokens, Predicate operatorPredicate, Predicate isSpecialOperator, Function>, AST> handleSpecialOperator) { if (tokens == null) { throw new NullPointerException("Tokens must not be null"); } else if (operatorPredicate == null) { throw new NullPointerException( "Operator predicate must not be null"); } else if (isSpecialOperator == null) { throw new NullPointerException( "Special operator determiner must not be null"); } GenHolder>, AST>> initialState = new GenHolder<>(new Pair<>(new LinkedList<>(), null)); tokens.forEach( new TokenTransformer<>(initialState, operatorPredicate, isSpecialOperator, handleSpecialOperator)); return initialState.unwrap((pair) -> { return pair.merge((queue, currentAST) -> currentAST); }); } }