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 */ 28 package org.antlr.runtime.tree; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** A generic list of elements tracked in an alternative to be used in 34 * a -> rewrite rule. We need to subclass to fill in the next() method, 35 * which returns either an AST node wrapped around a token payload or 36 * an existing subtree. 37 * 38 * Once you start next()ing, do not try to add more elements. It will 39 * break the cursor tracking I believe. 40 * 41 * @see org.antlr.runtime.tree.RewriteRuleSubtreeStream 42 * @see org.antlr.runtime.tree.RewriteRuleTokenStream 43 * 44 * TODO: add mechanism to detect/puke on modification after reading from stream 45 */ 46 public abstract class RewriteRuleElementStream { 47 /** Cursor 0..n-1. If singleElement!=null, cursor is 0 until you next(), 48 * which bumps it to 1 meaning no more elements. 49 */ 50 protected int cursor = 0; 51 52 /** Track single elements w/o creating a list. Upon 2nd add, alloc list */ 53 protected Object singleElement; 54 55 /** The list of tokens or subtrees we are tracking */ 56 protected List elements; 57 58 /** Once a node / subtree has been used in a stream, it must be dup'd 59 * from then on. Streams are reset after subrules so that the streams 60 * can be reused in future subrules. So, reset must set a dirty bit. 61 * If dirty, then next() always returns a dup. 62 * 63 * I wanted to use "naughty bit" here, but couldn't think of a way 64 * to use "naughty". 65 * 66 * TODO: unused? 67 */ 68 protected boolean dirty = false; 69 70 /** The element or stream description; usually has name of the token or 71 * rule reference that this list tracks. Can include rulename too, but 72 * the exception would track that info. 73 */ 74 protected String elementDescription; 75 protected TreeAdaptor adaptor; 76 77 public RewriteRuleElementStream(TreeAdaptor adaptor, String elementDescription) { 78 this.elementDescription = elementDescription; 79 this.adaptor = adaptor; 80 } 81 82 /** Create a stream with one element */ 83 public RewriteRuleElementStream(TreeAdaptor adaptor, 84 String elementDescription, 85 Object oneElement) 86 { 87 this(adaptor, elementDescription); 88 add(oneElement); 89 } 90 91 /** Create a stream, but feed off an existing list */ 92 public RewriteRuleElementStream(TreeAdaptor adaptor, 93 String elementDescription, 94 List elements) 95 { 96 this(adaptor, elementDescription); 97 this.singleElement = null; 98 this.elements = elements; 99 } 100 101 /** Reset the condition of this stream so that it appears we have 102 * not consumed any of its elements. Elements themselves are untouched. 103 * Once we reset the stream, any future use will need duplicates. Set 104 * the dirty bit. 105 */ 106 public void reset() { 107 cursor = 0; 108 dirty = true; 109 } 110 111 public void add(Object el) { 112 //System.out.println("add '"+elementDescription+"' is "+el); 113 if ( el==null ) { 114 return; 115 } 116 if ( elements!=null ) { // if in list, just add 117 elements.add(el); 118 return; 119 } 120 if ( singleElement == null ) { // no elements yet, track w/o list 121 singleElement = el; 122 return; 123 } 124 // adding 2nd element, move to list 125 elements = new ArrayList(5); 126 elements.add(singleElement); 127 singleElement = null; 128 elements.add(el); 129 } 130 131 /** Return the next element in the stream. If out of elements, throw 132 * an exception unless size()==1. If size is 1, then return elements[0]. 133 * Return a duplicate node/subtree if stream is out of elements and 134 * size==1. If we've already used the element, dup (dirty bit set). 135 */ 136 public Object nextTree() { 137 int n = size(); 138 if ( dirty || (cursor>=n && n==1) ) { 139 // if out of elements and size is 1, dup 140 Object el = _next(); 141 return dup(el); 142 } 143 // test size above then fetch 144 Object el = _next(); 145 return el; 146 } 147 148 /** do the work of getting the next element, making sure that it's 149 * a tree node or subtree. Deal with the optimization of single- 150 * element list versus list of size > 1. Throw an exception 151 * if the stream is empty or we're out of elements and size>1. 152 * protected so you can override in a subclass if necessary. 153 */ 154 protected Object _next() { 155 int n = size(); 156 if ( n ==0 ) { 157 throw new RewriteEmptyStreamException(elementDescription); 158 } 159 if ( cursor>= n) { // out of elements? 160 if ( n ==1 ) { // if size is 1, it's ok; return and we'll dup 161 return toTree(singleElement); 162 } 163 // out of elements and size was not 1, so we can't dup 164 throw new RewriteCardinalityException(elementDescription); 165 } 166 // we have elements 167 if ( singleElement!=null ) { 168 cursor++; // move cursor even for single element list 169 return toTree(singleElement); 170 } 171 // must have more than one in list, pull from elements 172 Object o = toTree(elements.get(cursor)); 173 cursor++; 174 return o; 175 } 176 177 /** When constructing trees, sometimes we need to dup a token or AST 178 * subtree. Dup'ing a token means just creating another AST node 179 * around it. For trees, you must call the adaptor.dupTree() unless 180 * the element is for a tree root; then it must be a node dup. 181 */ 182 protected abstract Object dup(Object el); 183 184 /** Ensure stream emits trees; tokens must be converted to AST nodes. 185 * AST nodes can be passed through unmolested. 186 */ 187 protected Object toTree(Object el) { 188 return el; 189 } 190 191 public boolean hasNext() { 192 return (singleElement != null && cursor < 1) || 193 (elements!=null && cursor < elements.size()); 194 } 195 196 public int size() { 197 int n = 0; 198 if ( singleElement != null ) { 199 n = 1; 200 } 201 if ( elements!=null ) { 202 return elements.size(); 203 } 204 return n; 205 } 206 207 public String getDescription() { 208 return elementDescription; 209 } 210 } 211