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