BufferedTreeNodeStream.java revision 324c4644fee44b9898524c09511bd33c3f12e2df
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.tree;
29
30import org.antlr.runtime.Token;
31import org.antlr.runtime.TokenStream;
32import org.antlr.runtime.misc.IntArray;
33import java.util.*;
34
35/** A buffered stream of tree nodes.  Nodes can be from a tree of ANY kind.
36 *
37 *  This node stream sucks all nodes out of the tree specified in
38 *  the constructor during construction and makes pointers into
39 *  the tree using an array of Object pointers. The stream necessarily
40 *  includes pointers to DOWN and UP and EOF nodes.
41 *
42 *  This stream knows how to mark/release for backtracking.
43 *
44 *  This stream is most suitable for tree interpreters that need to
45 *  jump around a lot or for tree parsers requiring speed (at cost of memory).
46 *  There is some duplicated functionality here with UnBufferedTreeNodeStream
47 *  but just in bookkeeping, not tree walking etc...
48 *
49 *  TARGET DEVELOPERS:
50 *
51 *  This is the old CommonTreeNodeStream that buffered up entire node stream.
52 *  No need to implement really as new CommonTreeNodeStream is much better
53 *  and covers what we need.
54 *
55 *  @see CommonTreeNodeStream
56 */
57public class BufferedTreeNodeStream implements TreeNodeStream {
58	public static final int DEFAULT_INITIAL_BUFFER_SIZE = 100;
59	public static final int INITIAL_CALL_STACK_SIZE = 10;
60
61    protected class StreamIterator implements Iterator {
62		int i = 0;
63		public boolean hasNext() {
64			return i<nodes.size();
65		}
66
67		public Object next() {
68			int current = i;
69			i++;
70			if ( current < nodes.size() ) {
71				return nodes.get(current);
72			}
73			return eof;
74		}
75
76		public void remove() {
77			throw new RuntimeException("cannot remove nodes from stream");
78		}
79	}
80
81	// all these navigation nodes are shared and hence they
82	// cannot contain any line/column info
83
84	protected Object down;
85	protected Object up;
86	protected Object eof;
87
88	/** The complete mapping from stream index to tree node.
89	 *  This buffer includes pointers to DOWN, UP, and EOF nodes.
90	 *  It is built upon ctor invocation.  The elements are type
91	 *  Object as we don't what the trees look like.
92	 *
93	 *  Load upon first need of the buffer so we can set token types
94	 *  of interest for reverseIndexing.  Slows us down a wee bit to
95	 *  do all of the if p==-1 testing everywhere though.
96	 */
97	protected List nodes;
98
99	/** Pull nodes from which tree? */
100	protected Object root;
101
102	/** IF this tree (root) was created from a token stream, track it. */
103	protected TokenStream tokens;
104
105	/** What tree adaptor was used to build these trees */
106	TreeAdaptor adaptor;
107
108	/** Reuse same DOWN, UP navigation nodes unless this is true */
109	protected boolean uniqueNavigationNodes = false;
110
111	/** The index into the nodes list of the current node (next node
112	 *  to consume).  If -1, nodes array not filled yet.
113	 */
114	protected int p = -1;
115
116	/** Track the last mark() call result value for use in rewind(). */
117	protected int lastMarker;
118
119	/** Stack of indexes used for push/pop calls */
120	protected IntArray calls;
121
122	public BufferedTreeNodeStream(Object tree) {
123		this(new CommonTreeAdaptor(), tree);
124	}
125
126	public BufferedTreeNodeStream(TreeAdaptor adaptor, Object tree) {
127		this(adaptor, tree, DEFAULT_INITIAL_BUFFER_SIZE);
128	}
129
130	public BufferedTreeNodeStream(TreeAdaptor adaptor, Object tree, int initialBufferSize) {
131		this.root = tree;
132		this.adaptor = adaptor;
133		nodes = new ArrayList(initialBufferSize);
134		down = adaptor.create(Token.DOWN, "DOWN");
135		up = adaptor.create(Token.UP, "UP");
136		eof = adaptor.create(Token.EOF, "EOF");
137	}
138
139	/** Walk tree with depth-first-search and fill nodes buffer.
140	 *  Don't do DOWN, UP nodes if its a list (t is isNil).
141	 */
142	protected void fillBuffer() {
143		fillBuffer(root);
144		//System.out.println("revIndex="+tokenTypeToStreamIndexesMap);
145		p = 0; // buffer of nodes intialized now
146	}
147
148	public void fillBuffer(Object t) {
149		boolean nil = adaptor.isNil(t);
150		if ( !nil ) {
151			nodes.add(t); // add this node
152		}
153		// add DOWN node if t has children
154		int n = adaptor.getChildCount(t);
155		if ( !nil && n>0 ) {
156			addNavigationNode(Token.DOWN);
157		}
158		// and now add all its children
159		for (int c=0; c<n; c++) {
160			Object child = adaptor.getChild(t,c);
161			fillBuffer(child);
162		}
163		// add UP node if t has children
164		if ( !nil && n>0 ) {
165			addNavigationNode(Token.UP);
166		}
167	}
168
169	/** What is the stream index for node? 0..n-1
170	 *  Return -1 if node not found.
171	 */
172	protected int getNodeIndex(Object node) {
173		if ( p==-1 ) {
174			fillBuffer();
175		}
176		for (int i = 0; i < nodes.size(); i++) {
177			Object t = (Object) nodes.get(i);
178			if ( t==node ) {
179				return i;
180			}
181		}
182		return -1;
183	}
184
185	/** As we flatten the tree, we use UP, DOWN nodes to represent
186	 *  the tree structure.  When debugging we need unique nodes
187	 *  so instantiate new ones when uniqueNavigationNodes is true.
188	 */
189	protected void addNavigationNode(final int ttype) {
190		Object navNode = null;
191		if ( ttype==Token.DOWN ) {
192			if ( hasUniqueNavigationNodes() ) {
193				navNode = adaptor.create(Token.DOWN, "DOWN");
194			}
195			else {
196				navNode = down;
197			}
198		}
199		else {
200			if ( hasUniqueNavigationNodes() ) {
201				navNode = adaptor.create(Token.UP, "UP");
202			}
203			else {
204				navNode = up;
205			}
206		}
207		nodes.add(navNode);
208	}
209
210	public Object get(int i) {
211		if ( p==-1 ) {
212			fillBuffer();
213		}
214		return nodes.get(i);
215	}
216
217	public Object LT(int k) {
218		if ( p==-1 ) {
219			fillBuffer();
220		}
221		if ( k==0 ) {
222			return null;
223		}
224		if ( k<0 ) {
225			return LB(-k);
226		}
227		//System.out.print("LT(p="+p+","+k+")=");
228		if ( (p+k-1) >= nodes.size() ) {
229			return eof;
230		}
231		return nodes.get(p+k-1);
232	}
233
234	public Object getCurrentSymbol() { return LT(1); }
235
236/*
237	public Object getLastTreeNode() {
238		int i = index();
239		if ( i>=size() ) {
240			i--; // if at EOF, have to start one back
241		}
242		System.out.println("start last node: "+i+" size=="+nodes.size());
243		while ( i>=0 &&
244			(adaptor.getType(get(i))==Token.EOF ||
245			 adaptor.getType(get(i))==Token.UP ||
246			 adaptor.getType(get(i))==Token.DOWN) )
247		{
248			i--;
249		}
250		System.out.println("stop at node: "+i+" "+nodes.get(i));
251		return nodes.get(i);
252	}
253*/
254
255	/** Look backwards k nodes */
256	protected Object LB(int k) {
257		if ( k==0 ) {
258			return null;
259		}
260		if ( (p-k)<0 ) {
261			return null;
262		}
263		return nodes.get(p-k);
264	}
265
266	public Object getTreeSource() {
267		return root;
268	}
269
270	public String getSourceName() {
271		return getTokenStream().getSourceName();
272	}
273
274	public TokenStream getTokenStream() {
275		return tokens;
276	}
277
278	public void setTokenStream(TokenStream tokens) {
279		this.tokens = tokens;
280	}
281
282	public TreeAdaptor getTreeAdaptor() {
283		return adaptor;
284	}
285
286	public void setTreeAdaptor(TreeAdaptor adaptor) {
287		this.adaptor = adaptor;
288	}
289
290	public boolean hasUniqueNavigationNodes() {
291		return uniqueNavigationNodes;
292	}
293
294	public void setUniqueNavigationNodes(boolean uniqueNavigationNodes) {
295		this.uniqueNavigationNodes = uniqueNavigationNodes;
296	}
297
298	public void consume() {
299		if ( p==-1 ) {
300			fillBuffer();
301		}
302		p++;
303	}
304
305	public int LA(int i) {
306		return adaptor.getType(LT(i));
307	}
308
309	public int mark() {
310		if ( p==-1 ) {
311			fillBuffer();
312		}
313		lastMarker = index();
314		return lastMarker;
315	}
316
317	public void release(int marker) {
318		// no resources to release
319	}
320
321	public int index() {
322		return p;
323	}
324
325	public void rewind(int marker) {
326		seek(marker);
327	}
328
329	public void rewind() {
330		seek(lastMarker);
331	}
332
333	public void seek(int index) {
334		if ( p==-1 ) {
335			fillBuffer();
336		}
337		p = index;
338	}
339
340	/** Make stream jump to a new location, saving old location.
341	 *  Switch back with pop().
342	 */
343	public void push(int index) {
344		if ( calls==null ) {
345			calls = new IntArray();
346		}
347		calls.push(p); // save current index
348		seek(index);
349	}
350
351	/** Seek back to previous index saved during last push() call.
352	 *  Return top of stack (return index).
353	 */
354	public int pop() {
355		int ret = calls.pop();
356		seek(ret);
357		return ret;
358	}
359
360	public void reset() {
361		p = 0;
362		lastMarker = 0;
363        if (calls != null) {
364            calls.clear();
365        }
366    }
367
368	public int size() {
369		if ( p==-1 ) {
370			fillBuffer();
371		}
372		return nodes.size();
373	}
374
375	public Iterator iterator() {
376		if ( p==-1 ) {
377			fillBuffer();
378		}
379		return new StreamIterator();
380	}
381
382	// TREE REWRITE INTERFACE
383
384	public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) {
385		if ( parent!=null ) {
386			adaptor.replaceChildren(parent, startChildIndex, stopChildIndex, t);
387		}
388	}
389
390	/** Used for testing, just return the token type stream */
391	public String toTokenTypeString() {
392		if ( p==-1 ) {
393			fillBuffer();
394		}
395		StringBuffer buf = new StringBuffer();
396		for (int i = 0; i < nodes.size(); i++) {
397			Object t = (Object) nodes.get(i);
398			buf.append(" ");
399			buf.append(adaptor.getType(t));
400		}
401		return buf.toString();
402	}
403
404	/** Debugging */
405	public String toTokenString(int start, int stop) {
406		if ( p==-1 ) {
407			fillBuffer();
408		}
409		StringBuffer buf = new StringBuffer();
410		for (int i = start; i < nodes.size() && i <= stop; i++) {
411			Object t = (Object) nodes.get(i);
412			buf.append(" ");
413			buf.append(adaptor.getToken(t));
414		}
415		return buf.toString();
416	}
417
418	public String toString(Object start, Object stop) {
419		System.out.println("toString");
420		if ( start==null || stop==null ) {
421			return null;
422		}
423		if ( p==-1 ) {
424			fillBuffer();
425		}
426		//System.out.println("stop: "+stop);
427		if ( start instanceof CommonTree )
428			System.out.print("toString: "+((CommonTree)start).getToken()+", ");
429		else
430			System.out.println(start);
431		if ( stop instanceof CommonTree )
432			System.out.println(((CommonTree)stop).getToken());
433		else
434			System.out.println(stop);
435		// if we have the token stream, use that to dump text in order
436		if ( tokens!=null ) {
437			int beginTokenIndex = adaptor.getTokenStartIndex(start);
438			int endTokenIndex = adaptor.getTokenStopIndex(stop);
439			// if it's a tree, use start/stop index from start node
440			// else use token range from start/stop nodes
441			if ( adaptor.getType(stop)==Token.UP ) {
442				endTokenIndex = adaptor.getTokenStopIndex(start);
443			}
444			else if ( adaptor.getType(stop)==Token.EOF ) {
445				endTokenIndex = size()-2; // don't use EOF
446			}
447			return tokens.toString(beginTokenIndex, endTokenIndex);
448		}
449		// walk nodes looking for start
450		Object t = null;
451		int i = 0;
452		for (; i < nodes.size(); i++) {
453			t = nodes.get(i);
454			if ( t==start ) {
455				break;
456			}
457		}
458		// now walk until we see stop, filling string buffer with text
459		 StringBuffer buf = new StringBuffer();
460		t = nodes.get(i);
461		while ( t!=stop ) {
462			String text = adaptor.getText(t);
463			if ( text==null ) {
464				text = " "+String.valueOf(adaptor.getType(t));
465			}
466			buf.append(text);
467			i++;
468			t = nodes.get(i);
469		}
470		// include stop node too
471		String text = adaptor.getText(stop);
472		if ( text==null ) {
473			text = " "+String.valueOf(adaptor.getType(stop));
474		}
475		buf.append(text);
476		return buf.toString();
477	}
478}
479