summaryrefslogtreecommitdiff
path: root/base/src/main/java/bjc/utils/parserutils/TreeConstructor.java
blob: bd907b5efe5c5a8c378b587d01783fd589fc0228 (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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
package bjc.utils.parserutils;

import java.util.Deque;
import java.util.LinkedList;
import java.util.function.*;

import bjc.data.*;
import bjc.funcdata.ListEx;
import bjc.utils.parserutils.TreeConstructor.*;

/**
 * Creates a parse tree from a postfix expression.
 *
 * @author ben
 *
 */
public class TreeConstructor {
	/**
	 * Alias interface for special operator types.
	 *
	 * @param <TokenType>
	 *                    The token type of the tree.
	 */
	public interface QueueFlattener<TokenType>
			extends Function<Deque<Tree<TokenType>>, Tree<TokenType>> {
		/*
		 * Alias
		 */
	}

	/* Alias for constructor state. */
	static final class ConstructorState<TokenType>
			extends SimplePair<Deque<Tree<TokenType>>, Tree<TokenType>> {
		public ConstructorState(final Deque<Tree<TokenType>> left,
				final Tree<TokenType> right) {
			super(left, right);
		}

		public ConstructorState(
				final Pair<Deque<Tree<TokenType>>, Tree<TokenType>> par) {
			super(par.getLeft(), par.getRight());
		}
	}

	/**
	 * Construct a tree from a list of tokens in postfix notation.
	 *
	 * Only binary operators are accepted.
	 *
	 * @param <TokenType>
	 *                    The elements of the parse tree.
	 *
	 * @param tokens
	 *                    The list of tokens to build a tree from.
	 *
	 * @param isOperator
	 *                    The predicate to use to determine if something is a
	 *                    operator.
	 *
	 * @return A AST from the expression.
	 */
	public static <TokenType> Tree<TokenType> constructTree(
			final ListEx<TokenType> tokens, final Predicate<TokenType> isOperator) {
		/* Construct a tree with no special operators */
		return constructTree(tokens, isOperator, 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 <TokenType>
	 *                              The elements of the parse tree.
	 *
	 * @param tokens
	 *                              The list of tokens to build a tree from.
	 *
	 * @param isOperator
	 *                              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.
	 *
	 */
	public static <TokenType> Tree<TokenType> constructTree(
			final ListEx<TokenType> tokens, final Predicate<TokenType> isOperator,
			final Predicate<TokenType> isSpecialOperator,
			final Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator) {
		/*
		 * Make sure our parameters are valid
		 */
		if (tokens == null) {
			throw new NullPointerException("Tokens must not be null");
		} else if (isOperator == null) {
			throw new NullPointerException("Operator predicate must not be null");
		} else if (isSpecialOperator == null) {
			throw new NullPointerException(
					"Special operator determiner must not be null");
		}

		final ConstructorState<TokenType> cstate
				= new ConstructorState<>(new LinkedList<>(), null);

		/* Here is the state for the tree construction */
		final Holder<ConstructorState<TokenType>> initialState = new Identity<>(cstate);

		/* Transform each of the tokens */
		final TokenTransformer<TokenType> trans = new TokenTransformer<>(initialState,
				isOperator, isSpecialOperator, handleSpecialOperator);

		tokens.forEach(trans);

		/* Grab the tree from the state */
		return initialState.unwrap(ConstructorState::getRight);
	}
}

/*
 * Transform function on tokens
 */
class TokenTransformer<TokenType> implements Consumer<TokenType> {
	/*
	 * Handle operators
	 */
	private final class OperatorHandler
			implements UnaryOperator<ConstructorState<TokenType>> {
		/* The handled element. */
		private final TokenType element;

		/* Create a new operator handler. */
		public OperatorHandler(final TokenType element) {
			this.element = element;
		}

		@Override
		public ConstructorState<TokenType> apply(final ConstructorState<TokenType> pair) {
			/*
			 * Replace the current AST with the result of handling an operator
			 */
			return new ConstructorState<>(
					pair.bindLeft(queuedASTs -> handleOperator(queuedASTs)));
		}

		private ConstructorState<TokenType>
				handleOperator(final Deque<Tree<TokenType>> queuedASTs) {
			/*
			 * The AST we're going to hand back
			 */
			Tree<TokenType> newAST;

			/*
			 * Handle special operators
			 */
			if (isSpecialOperator.test(element)) {
				newAST = handleSpecialOperator.apply(element).apply(queuedASTs);
			} else {
				/*
				 * Error if we don't have enough for a binary operator
				 */
				if (queuedASTs.size() < 2) {
					final String msg = String.format(
							"Attempted to parse binary operator without enough operands\n\tProblem operator is: %s\n\tPossible operand is: %s",
							element.toString(), queuedASTs.peek().toString());

					throw new IllegalStateException(msg);
				}

				/*
				 * Grab the two operands
				 */
				final Tree<TokenType> right = queuedASTs.pop();
				final Tree<TokenType> left = queuedASTs.pop();

				/*
				 * Create a new AST
				 */
				newAST = new SimpleTree<>(element, left, right);
			}

			/*
			 * Stick it onto the stack
			 */
			queuedASTs.push(newAST);

			/*
			 * Hand back the state
			 */
			return new ConstructorState<>(queuedASTs, newAST);
		}
	}

	/* The initial state of the transformer. */
	private final Holder<ConstructorState<TokenType>> initialState;

	/* The predicate tot use to detect operators. */
	private final Predicate<TokenType> operatorPredicate;

	/* The predicate for detecting special operators. */
	private final Predicate<TokenType> isSpecialOperator;
	/* The function for handling special operators. */
	private final Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator;

	/**
	 * Create a new transformer
	 *
	 * @param initialState
	 *                              The initial state of the transformer.
	 *
	 * @param operatorPredicate
	 *                              The predicate to use to identify operators.
	 *
	 * @param isSpecialOperator
	 *                              The predicate used to identify special
	 *                              operators.
	 *
	 * @param handleSpecialOperator
	 *                              The function used for handling special
	 *                              operators.
	 */
	public TokenTransformer(final Holder<ConstructorState<TokenType>> initialState,
			final Predicate<TokenType> operatorPredicate,
			final Predicate<TokenType> isSpecialOperator,
			final Function<TokenType, QueueFlattener<TokenType>> handleSpecialOperator) {
		this.initialState = initialState;
		this.operatorPredicate = operatorPredicate;
		this.isSpecialOperator = isSpecialOperator;
		this.handleSpecialOperator = handleSpecialOperator;
	}

	@Override
	public void accept(final TokenType element) {
		/*
		 * Handle operators
		 */
		if (operatorPredicate.test(element)) {
			initialState.transform(new OperatorHandler(element));
		} else {
			final Tree<TokenType> newAST = new SimpleTree<>(element);

			/*
			 * Insert the new tree into the AST
			 */
			initialState.transform(pair -> new ConstructorState<>(
					pair.bindLeft(queue -> {
						queue.push(newAST);

						return new SimplePair<>(queue, newAST);
					})
				)
			);
		}
	}
}