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.ANTLRInputStream;
8 
9 import antlr.v4.runtime.CharStream;
10 import antlr.v4.runtime.IntStream;
11 import antlr.v4.runtime.IntStreamConstant;
12 import antlr.v4.runtime.misc.Interval;
13 import std.algorithm;
14 import std.conv;
15 import std.file;
16 import std.format;
17 import std.stdio;
18 
19 /**
20  * Vacuum all input from a {@link Reader}/{@link InputStream} and then treat it
21  * like a {@code char[]} buffer. Can also pass in a {@link String} or
22  * {@code char[]} to use.
23  *
24  * <p>If you need encoding, pass in stream/reader with correct encoding.</p>
25  */
26 class ANTLRInputStream : CharStream
27 {
28 
29     public static int READ_BUFFER_SIZE = 1024;
30 
31     public static int INITIAL_BUFFER_SIZE = 1024;;
32 
33     /**
34      * The data being scanned
35      */
36     protected char[] data;
37 
38     /**
39      * How many characters are actually in the buffer
40      */
41     protected int n;
42 
43     /**
44      * 0..n-1 index into string of next char
45      */
46     protected int p = 0;
47 
48     /**
49      * What is name or source of this char stream?
50      */
51     public string name;
52 
53     public this()
54     {
55     }
56 
57     /**
58      * Copy data in string to a local char array
59      */
60     public this(string input)
61     {
62         this.data = input.to!(char []);
63         this.n = to!int(input.length);
64     }
65 
66     /**
67      * This is the preferred constructor for strings as no data is copied
68      */
69     public this(char[] data, int numberOfActualCharsInArray)
70     {
71         this.data = data;
72         this.n = numberOfActualCharsInArray;
73     }
74 
75     public this(File r)
76     {
77         load(r, INITIAL_BUFFER_SIZE, READ_BUFFER_SIZE);
78     }
79 
80     public void load(File r, int size, int readChunkSize)
81     {
82         debug (ANTLRInputStream)
83             writefln("load %1$s in chunks of %2$s", size, readChunkSize);
84         name = r.name;
85         data = to!(char[])(name.readText);
86         // set the actual size of the data available;
87         n = to!int(data.length);
88         debug (ANTLRInputStreamStream)
89             writefln("n= $s", n);
90     }
91 
92     /**
93      * Reset the stream so that it's in the same state it was
94      * when the object was created *except* the data array is not
95      * touched.
96      */
97     public void reset()
98     {
99         p = 0;
100     }
101 
102     /**
103      * @uml
104      * @override
105      */
106     public override void consume()
107     {
108 	if (p >= n) {
109             assert (LA(1) == IntStreamConstant.EOF, "cannot consume EOF");
110         }
111         //System.out.println("prev p="+p+", c="+(char)data[p]);
112         if (p < n) {
113             p++;
114             //System.out.println("p moves to "+p+" (c='"+(char)data[p]+"')");
115         }
116     }
117 
118     /**
119      * @uml
120      * @override
121      */
122     public override int LA(int i)
123     {
124         if (i == 0) {
125             return 0; // undefined
126         }
127         if (i < 0) {
128             i++; // e.g., translate LA(-1) to use offset i=0; then data[p+0-1]
129             if ((p + i - 1) < 0) {
130                 return IntStreamConstant.EOF; // invalid; no char before first char
131             }
132         }
133         if (( p + i - 1) >= n) {
134             debug (ANTLRInputStream) {
135                 import std.stdio;
136                 writefln("char LA(%s)=EOF; p=%s", i, p);
137             }
138             return IntStreamConstant.EOF;
139         }
140         debug (ANTLRInputStream) {
141                 import std.stdio;
142                 writefln("LA(%s); p=%s n=%s data.length=%s", i, p, n, data.length);
143                 writefln("char LA(%s)=%s; p=%s", i, data[p+i-1], p);
144             }
145         return data[p+i-1];
146     }
147 
148     public int LT(int i)
149     {
150         return LA(i);
151     }
152 
153     /**
154      * @uml
155      * @override
156      */
157     public override int index()
158     {
159         return p;
160     }
161 
162     /**
163      * @uml
164      * @override
165      */
166     public override int size()
167     {
168         return n;
169     }
170 
171     /**
172      * mark/release do nothing; we have entire buffer
173      * @uml
174      * @override
175      */
176     public override int mark()
177     {
178         return -1;
179     }
180 
181     /**
182      * @uml
183      * @override
184      */
185     public override void release(int marker)
186     {
187     }
188 
189     /**
190      * consume() ahead until p==index; can't just set p=index as we must
191      * update line and charPositionInLine. If we seek backwards, just set p
192      * @uml
193      * @override
194      */
195     public override void seek(int index)
196     {
197 	if (index <= p) {
198             p = index; // just jump; don't update stream state (line, ...)
199             return;
200         }
201         // seek forward, consume until p hits index or n (whichever comes first)
202         index = min(index, n);
203         while (p < index) {
204             consume();
205         }
206     }
207 
208     /**
209      * @uml
210      * @override
211      */
212     public override string getText(Interval interval)
213     {
214         int start = interval.a;
215         int stop = interval.b;
216         if (stop >= n)
217             stop = n-1;
218         if (start >= n) return "";
219         debug (ANTLRInputStream) {
220             writefln("data: %s, n=%s, start=%s, stop=%s",
221                      data[start..stop+1], n, start, stop);
222         }
223         return data[start..stop+1].idup();
224     }
225 
226     /**
227      * @uml
228      * @override
229      */
230     public override string getSourceName()
231     {
232         if (!name) {
233             return IntStreamConstant.UNKNOWN_SOURCE_NAME;
234         }
235         return name;
236     }
237 
238     /**
239      * @uml
240      * @override
241      */
242     public override string toString()
243     {
244         return to!string(data);
245     }
246 
247 }