summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java
blob: 30ccc5a7dd8bf9fe925e24c6e62fa7cd8911e627 (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
package bjc.utils.parserutils;

import java.util.Deque;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;

import bjc.utils.data.IHolder;
import bjc.utils.data.ITree;
import bjc.utils.data.Pair;
import bjc.utils.data.Tree;
import bjc.utils.parserutils.TreeConstructor.ConstructorState;
import bjc.utils.parserutils.TreeConstructor.QueueFlattener;

/*
 * Handle creating ASTs from tokens.
 */
final class TokenTransformer<TokenType> implements Consumer<TokenType> {
	/*
	 * Handle operators
	 */
	private final class OperatorHandler implements UnaryOperator<ConstructorState<TokenType>> {
		private final TokenType element;

		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 -> {
				return handleOperator(queuedASTs);
			}));
		}

		private ConstructorState<TokenType> handleOperator(final Deque<ITree<TokenType>> queuedASTs) {
			/*
			 * The AST we're going to hand back
			 */
			ITree<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 ITree<TokenType> right = queuedASTs.pop();
				final ITree<TokenType> left = queuedASTs.pop();

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

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

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

	private final IHolder<ConstructorState<TokenType>> initialState;

	private final Predicate<TokenType> operatorPredicate;

	private final Predicate<TokenType>				isSpecialOperator;
	private final Function<TokenType, QueueFlattener<TokenType>>	handleSpecialOperator;

	/*
	 * Create a new transformer
	 */
	public TokenTransformer(final IHolder<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 ITree<TokenType> newAST = new Tree<>(element);

			/*
			 * Insert the new tree into the AST
			 */
			initialState.transform(pair -> {
				/*
				 * Transform the pair, ignoring the current AST in favor of the one consisting of the current element
				 */
				return new ConstructorState<>(pair.bindLeft(queue -> {
					queue.push(newAST);

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