1/*
2 [The "BSD license"]
3 Copyright (c) 2005-2009 Terence Parr
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 1. Redistributions of source code must retain the above copyright
10     notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12     notice, this list of conditions and the following disclaimer in the
13     documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15     derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28package org.antlr.runtime;
29
30import java.util.ArrayList;
31import java.util.List;
32
33/** A pretty quick CharStream that pulls all data from an array
34 *  directly.  Every method call counts in the lexer.  Java's
35 *  strings aren't very good so I'm avoiding.
36 */
37public class ANTLRStringStream implements CharStream {
38	/** The data being scanned */
39	protected char[] data;
40
41	/** How many characters are actually in the buffer */
42	protected int n;
43
44	/** 0..n-1 index into string of next char */
45	protected int p=0;
46
47	/** line number 1..n within the input */
48	protected int line = 1;
49
50	/** The index of the character relative to the beginning of the line 0..n-1 */
51	protected int charPositionInLine = 0;
52
53	/** tracks how deep mark() calls are nested */
54	protected int markDepth = 0;
55
56	/** A list of CharStreamState objects that tracks the stream state
57	 *  values line, charPositionInLine, and p that can change as you
58	 *  move through the input stream.  Indexed from 1..markDepth.
59     *  A null is kept @ index 0.  Create upon first call to mark().
60	 */
61	protected List markers;
62
63	/** Track the last mark() call result value for use in rewind(). */
64	protected int lastMarker;
65
66	/** What is name or source of this char stream? */
67	public String name;
68
69	public ANTLRStringStream() {
70	}
71
72	/** Copy data in string to a local char array */
73	public ANTLRStringStream(String input) {
74		this();
75		this.data = input.toCharArray();
76		this.n = input.length();
77	}
78
79	/** This is the preferred constructor as no data is copied */
80	public ANTLRStringStream(char[] data, int numberOfActualCharsInArray) {
81		this();
82		this.data = data;
83		this.n = numberOfActualCharsInArray;
84	}
85
86	/** Reset the stream so that it's in the same state it was
87	 *  when the object was created *except* the data array is not
88	 *  touched.
89	 */
90	public void reset() {
91		p = 0;
92		line = 1;
93		charPositionInLine = 0;
94		markDepth = 0;
95	}
96
97    public void consume() {
98		//System.out.println("prev p="+p+", c="+(char)data[p]);
99        if ( p < n ) {
100			charPositionInLine++;
101			if ( data[p]=='\n' ) {
102				/*
103				System.out.println("newline char found on line: "+line+
104								   "@ pos="+charPositionInLine);
105				*/
106				line++;
107				charPositionInLine=0;
108			}
109            p++;
110			//System.out.println("p moves to "+p+" (c='"+(char)data[p]+"')");
111        }
112    }
113
114    public int LA(int i) {
115		if ( i==0 ) {
116			return 0; // undefined
117		}
118		if ( i<0 ) {
119			i++; // e.g., translate LA(-1) to use offset i=0; then data[p+0-1]
120			if ( (p+i-1) < 0 ) {
121				return CharStream.EOF; // invalid; no char before first char
122			}
123		}
124
125		if ( (p+i-1) >= n ) {
126            //System.out.println("char LA("+i+")=EOF; p="+p);
127            return CharStream.EOF;
128        }
129        //System.out.println("char LA("+i+")="+(char)data[p+i-1]+"; p="+p);
130		//System.out.println("LA("+i+"); p="+p+" n="+n+" data.length="+data.length);
131		return data[p+i-1];
132    }
133
134	public int LT(int i) {
135		return LA(i);
136	}
137
138	/** Return the current input symbol index 0..n where n indicates the
139     *  last symbol has been read.  The index is the index of char to
140	 *  be returned from LA(1).
141     */
142    public int index() {
143        return p;
144    }
145
146	public int size() {
147		return n;
148	}
149
150	public int mark() {
151        if ( markers==null ) {
152            markers = new ArrayList();
153            markers.add(null); // depth 0 means no backtracking, leave blank
154        }
155        markDepth++;
156		CharStreamState state = null;
157		if ( markDepth>=markers.size() ) {
158			state = new CharStreamState();
159			markers.add(state);
160		}
161		else {
162			state = (CharStreamState)markers.get(markDepth);
163		}
164		state.p = p;
165		state.line = line;
166		state.charPositionInLine = charPositionInLine;
167		lastMarker = markDepth;
168		return markDepth;
169    }
170
171    public void rewind(int m) {
172		CharStreamState state = (CharStreamState)markers.get(m);
173		// restore stream state
174		seek(state.p);
175		line = state.line;
176		charPositionInLine = state.charPositionInLine;
177		release(m);
178	}
179
180	public void rewind() {
181		rewind(lastMarker);
182	}
183
184	public void release(int marker) {
185		// unwind any other markers made after m and release m
186		markDepth = marker;
187		// release this marker
188		markDepth--;
189	}
190
191	/** consume() ahead until p==index; can't just set p=index as we must
192	 *  update line and charPositionInLine.
193	 */
194	public void seek(int index) {
195		if ( index<=p ) {
196			p = index; // just jump; don't update stream state (line, ...)
197			return;
198		}
199		// seek forward, consume until p hits index
200		while ( p<index ) {
201			consume();
202		}
203	}
204
205	public String substring(int start, int stop) {
206		return new String(data,start,stop-start+1);
207	}
208
209	public int getLine() {
210		return line;
211	}
212
213	public int getCharPositionInLine() {
214		return charPositionInLine;
215	}
216
217	public void setLine(int line) {
218		this.line = line;
219	}
220
221	public void setCharPositionInLine(int pos) {
222		this.charPositionInLine = pos;
223	}
224
225	public String getSourceName() {
226		return name;
227	}
228
229    public String toString() { return new String(data); }
230}
231