1 /*
2  * Copyright (c) 2012-2019 The ANTLR Project. All rights reserved.
3  * Use of this file is governed by the BSD 3-clause license that
4  * can be found in the LICENSE.txt file in the project root.
5  */
6 
7 module antlr.v4.runtime.ListTokenSource;
8 
9 import antlr.v4.runtime.CharStream;
10 import antlr.v4.runtime.CommonToken;
11 import antlr.v4.runtime.CommonTokenFactory;
12 import antlr.v4.runtime.Token;
13 import antlr.v4.runtime.TokenConstantDefinition;
14 import antlr.v4.runtime.TokenFactory;
15 import antlr.v4.runtime.TokenSource;
16 import std.algorithm;
17 import std.conv;
18 import std.string;
19 import std.typecons;
20 import std.variant;
21 
22 alias TokenFactorySourcePair = Tuple!(TokenSource, "a", CharStream, "b");
23 
24 /**
25  * Provides an implementation of {@link TokenSource} as a wrapper around a list
26  * of {@link Token} objects.
27  *
28  * <p>If the final token in the list is an {@link Token#EOF} token, it will be used
29  * as the EOF token for every call to {@link #nextToken} after the end of the
30  * list is reached. Otherwise, an EOF token will be created.</p>
31  */
32 class ListTokenSource : TokenSource
33 {
34 
35     /**
36      * The wrapped collection of {@link Token} objects to return.
37      */
38     public Token[] tokens;
39 
40     /**
41      * The name of the input source. If this value is {@code null}, a call to
42      * {@link #getSourceName} should return the source name used to create the
43      * the next token in {@link #tokens} (or the previous token if the end of
44      * the input has been reached).
45      */
46     private string sourceName;
47 
48     /**
49      * The index into {@link #tokens} of token to return by the next call to
50      * {@link #nextToken}. The end of the input is indicated by this value
51      * being greater than or equal to the number of items in {@link #tokens}.
52      */
53     protected int i;
54 
55     /**
56      * This field caches the EOF token for the token source.
57      */
58     protected Token eofToken;
59 
60     /**
61      * This is the backing field for {@link #getTokenFactory} and
62      *  {@link setTokenFactory}.
63      */
64     public TokenFactory!CommonToken _factory;
65 
66     /**
67      * Constructs a new {@link ListTokenSource} instance from the specified
68      * collection of {@link Token} objects.
69      *
70      *  @param tokens The collection of {@link Token} objects to provide as a
71      *  {@link TokenSource}.
72      *  @exception NullPointerException if {@code tokens} is {@code null}
73      */
74     public this(Token[] tokens)
75     {
76         this(tokens, null);
77     }
78 
79     /**
80      * Constructs a new {@link ListTokenSource} instance from the specified
81      * collection of {@link Token} objects and source name.
82      *
83      *  @param tokens The collection of {@link Token} objects to provide as a
84      * {@link TokenSource}.
85      *  @param sourceName The name of the {@link TokenSource}. If this value is
86      * {@code null}, {@link #getSourceName} will attempt to infer the name from
87      * the next {@link Token} (or the previous token if the end of the input has
88      * been reached).
89      *
90      *  @exception NullPointerException if {@code tokens} is {@code null}
91      */
92     public this(Token[] tokens, string sourceName)
93     in
94     {
95         assert(tokens !is null, "tokens cannot be null");
96     }
97     do
98     {
99         _factory = CommonTokenFactory.DEFAULT;
100         this.tokens = tokens;
101         this.sourceName = sourceName;
102     }
103 
104     public int getCharPositionInLine()
105     {
106 	if (i < tokens.length) {
107             return tokens[i].getCharPositionInLine();
108         }
109 		else if (eofToken !is null) {
110 			return eofToken.getCharPositionInLine();
111 		}
112 		else if (tokens.length > 0) {
113 			// have to calculate the result from the line/column of the previous
114 			// token, along with the text of the token.
115 			Token lastToken = tokens[$ - 1];
116 			auto tokenText = lastToken.getText();
117                         Variant Null;
118 			if (tokenText !is Null) {
119                             auto lastNewLine = (to!string(tokenText)).lastIndexOf('\n');
120 				if (lastNewLine >= 0) {
121                                     return to!int((tokenText.length) - lastNewLine - 1);
122 				}
123 			}
124 
125 			return lastToken.getCharPositionInLine() + lastToken.stopIndex - lastToken.startIndex + 1;
126 		}
127 
128 		// only reach this if tokens is empty, meaning EOF occurs at the first
129 		// position in the input
130 		return 0;
131     }
132 
133     public Token nextToken()
134     {
135 	if (i >= tokens.length) {
136             if (eofToken is null) {
137                 int start = -1;
138                 if (tokens.length > 0) {
139                     int previousStop = tokens[$ - 1].stopIndex;
140                     if (previousStop != -1) {
141                         start = previousStop + 1;
142                     }
143                 }
144 
145                 int stop = max(-1, start - 1);
146                 TokenFactorySourcePair tfsp = tuple(this, getInputStream());
147                 Variant v = "EOF";
148                 eofToken = _factory.create(tfsp, TokenConstantDefinition.EOF, v, TokenConstantDefinition.DEFAULT_CHANNEL, start, stop, getLine(), getCharPositionInLine());
149             }
150             return eofToken;
151         }
152 
153         Token t = tokens[i];
154         if (i == tokens.length - 1 && t.getType() == TokenConstantDefinition.EOF) {
155             eofToken = t;
156         }
157 
158         i++;
159         return t;
160     }
161 
162     public int getLine()
163     {
164 	if (i < tokens.length) {
165             return tokens[i].getLine();
166         }
167         else if (eofToken !is null) {
168             return eofToken.getLine();
169         }
170         else if (tokens.length > 0) {
171             // have to calculate the result from the line/column of the previous
172             // token, along with the text of the token.
173             Token lastToken = tokens[tokens.length - 1];
174             int line = lastToken.getLine();
175 
176             string tokenText = to!string(lastToken.getText);
177             if (tokenText !is null) {
178                 foreach (c; tokenText) {
179                     if (c == '\n') {
180                         line++;
181                     }
182                 }
183             }
184 
185             // if no text is available, assume the token did not contain any newline characters.
186             return line;
187         }
188 
189         // only reach this if tokens is empty, meaning EOF occurs at the first
190         // position in the input
191         return 1;
192     }
193 
194     public CharStream getInputStream()
195     {
196 	if (i < tokens.length) {
197             return tokens[i].getInputStream();
198         }
199         else if (eofToken !is null) {
200             return eofToken.getInputStream();
201         }
202         else if (tokens.length > 0) {
203             return tokens[tokens.length - 1].getInputStream();
204         }
205         // no input stream information is available
206         return null;
207     }
208 
209     public string getSourceName()
210     {
211         if (sourceName !is null) {
212             return sourceName;
213         }
214 
215         CharStream inputStream = getInputStream();
216         if (inputStream !is null) {
217             return inputStream.getSourceName();
218         }
219 
220         return "List";
221     }
222 
223     public void tokenFactory(TokenFactory!CommonToken factory)
224     {
225         this._factory = factory;
226     }
227 
228     public TokenFactory!CommonToken tokenFactory()
229     {
230         return _factory;
231     }
232 
233 }