aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java
blob: 5b91a6cc8f4a1b8739e1a1414f971ed3e0f1dca7 (plain) (blame)
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
/*
 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
 *
 * SPDX-License-Identifier: EPL-2.0
 */

/*
 * generated by Xtext 2.29.0.M2
 */
package tools.refinery.language.parser.antlr;

import com.google.inject.Inject;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenSource;
import tools.refinery.language.parser.antlr.internal.InternalProblemParser;

import java.util.ArrayDeque;
import java.util.Deque;

public class ProblemTokenSource implements TokenSource {
	private IdentifierTokenProvider identifierTokenProvider;

	private final TokenSource delegate;

	private final Deque<Token> buffer = new ArrayDeque<>();

	private boolean recursive;

	private boolean seenId;

	public ProblemTokenSource(TokenSource delegate) {
		this.delegate = delegate;
	}

	@Inject
	public void setIdentifierTokenProvider(IdentifierTokenProvider identifierTokenProvider) {
		this.identifierTokenProvider = identifierTokenProvider;
	}

	public boolean isRecursive() {
		return recursive;
	}

	public void setRecursive(boolean recursive) {
		this.recursive = recursive;
	}

	@Override
	public Token nextToken() {
		if (!buffer.isEmpty()) {
			return buffer.removeFirst();
		}
		var token = delegate.nextToken();
		if (isIdentifier(token)) {
			seenId = true;
		} else if (seenId && isPlusOrTransitiveClosure(token)) {
			if (peekForTransitiveClosure()) {
				token.setType(InternalProblemParser.RULE_TRANSITIVE_CLOSURE);
			}
		} else if (isVisibleToken(token)) {
			seenId = false;
		}
		return token;
	}

	@Override
	public String getSourceName() {
		return "[%s]%s".formatted(this.getClass().getSimpleName(), delegate.getSourceName());
	}

	protected boolean isIdentifier(Token token) {
		return identifierTokenProvider.isIdentifierToken(token.getType());
	}

	protected boolean isPlusOrTransitiveClosure(Token token) {
		return token.getType() == InternalProblemParser.PlusSign;
	}

	protected boolean isVisibleToken(Token token) {
		int tokenId = token.getType();
		return tokenId != InternalProblemParser.RULE_WS && tokenId != InternalProblemParser.RULE_SL_COMMENT &&
				tokenId != InternalProblemParser.RULE_ML_COMMENT;
	}

	protected boolean peekForTransitiveClosure() {
		Token token = peekWithSkipWhitespace();
		if (token.getType() != InternalProblemParser.LeftParenthesis) {
			return false;
		}
		while (true) {
			token = peekWithSkipWhitespace();
			if (!isIdentifier(token)) {
				return false;
			}
			token = peekWithSkipWhitespace();
			switch (token.getType()) {
			case InternalProblemParser.Comma:
				return true;
			case InternalProblemParser.ColonColon:
				break;
			default:
				// By default, we do not peek at inner plus signs to limit recursion depth.
				// Such expressions are never valid, so we don't have to parse them correctly.
				if (recursive && isPlusOrTransitiveClosure(token) && peekForTransitiveClosure()) {
					token.setType(InternalProblemParser.RULE_TRANSITIVE_CLOSURE);
				}
				// Not a transitive closure for the initial position where we started peeking.
				return false;
			}
		}
	}

	protected Token peekToken() {
		var token = delegate.nextToken();
		if (isIdentifier(token)) {
			seenId = true;
		} else if (isVisibleToken(token)) {
			seenId = false;
		}
		buffer.addLast(token);
		return token;
	}

	protected Token peekWithSkipWhitespace() {
		Token token;
		do {
			token = peekToken();
		} while (token != null && !isVisibleToken(token));
		return token;
	}
}