summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java
blob: 088430b8e75528bf0a2fa629ddf969e885433c98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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 java.util.function.UnaryOperator;

import bjc.utils.data.experimental.IHolder;
import bjc.utils.data.experimental.IPair;
import bjc.utils.data.experimental.Identity;
import bjc.utils.data.experimental.Pair;
import bjc.utils.funcdata.IFunctionalList;

/**
 * Creates a parse tree from a postfix expression
 * 
 * @author ben
 *
 */
public class TreeConstructor {
	private static final class TokenTransformer<T> implements Consumer<T> {
		private final class OperatorHandler
				implements UnaryOperator<IPair<Deque<AST<T>>, AST<T>>> {
			private T element;

			public OperatorHandler(T element) {
				this.element = element;
			}

			@Override
			public IPair<Deque<AST<T>>, AST<T>> apply(
					IPair<Deque<AST<T>>, AST<T>> pair) {
				return pair.bind((queuedASTs, currentAST) -> {
					return handleOperator(queuedASTs);
				});
			}

			private IPair<Deque<AST<T>>, AST<T>> handleOperator(
					Deque<AST<T>> queuedASTs) {
				AST<T> newAST;

				if (isSpecialOperator.test(element)) {
					newAST = handleSpecialOperator.apply(queuedASTs);
				} else {
					if (queuedASTs.size() < 2) {
						throw new IllegalStateException(
								"Attempted to parse binary operator without enough operands.\n"
										+ "Problem operator is " + element
										+ "\nPossible operand is: \n\t"
										+ queuedASTs.peek());
					}

					AST<T> rightAST = queuedASTs.pop();
					AST<T> leftAST = queuedASTs.pop();

					newAST = new AST<>(element, leftAST, rightAST);
				}

				queuedASTs.push(newAST);

				return new Pair<>(queuedASTs, newAST);
			}
		}

		private IHolder<IPair<Deque<AST<T>>, AST<T>>>	initialState;
		private Predicate<T>							operatorPredicate;
		private Predicate<T>							isSpecialOperator;
		private Function<Deque<AST<T>>, AST<T>>			handleSpecialOperator;

		public TokenTransformer(
				IHolder<IPair<Deque<AST<T>>, AST<T>>> initialState,
				Predicate<T> operatorPredicate,
				Predicate<T> isSpecialOperator,
				Function<Deque<AST<T>>, AST<T>> 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<T> newAST = new AST<>(element);

				initialState.doWith((pair) -> {
					pair.doWith((queue, currentAST) -> {
						queue.push(newAST);
					});
				});

				initialState.transform((pair) -> {
					return pair.bind((queue, currentAST) -> {
						return new Pair<>(queue, newAST);
					});
				});
			}
		}
	}

	/**
	 * Construct a tree from a list of tokens in postfix notation
	 * 
	 * Only binary operators are accepted.
	 * 
	 * @param <T>
	 *            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 <T> AST<T> constructTree(IFunctionalList<T> tokens,
			Predicate<T> 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 <T>
	 *            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 <T> AST<T> constructTree(IFunctionalList<T> tokens,
			Predicate<T> operatorPredicate, Predicate<T> isSpecialOperator,
			Function<Deque<AST<T>>, AST<T>> 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");
		}

		IHolder<IPair<Deque<AST<T>>, AST<T>>> initialState = new Identity<>(
				new Pair<>(new LinkedList<>(), null));

		tokens.forEach(
				new TokenTransformer<>(initialState, operatorPredicate,
						isSpecialOperator, handleSpecialOperator));

		return initialState.unwrap((pair) -> {
			return pair.merge((queue, currentAST) -> currentAST);
		});
	}
}