1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2005-2008 Terence Parr 4 * All rights reserved. 5 * 6 * Conversion to C#: 7 * Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33namespace Antlr.Runtime.Tree { 34 using System.Collections.Generic; 35 using IList = System.Collections.IList; 36 37 /** <summary> 38 * A generic list of elements tracked in an alternative to be used in 39 * a -> rewrite rule. We need to subclass to fill in the next() method, 40 * which returns either an AST node wrapped around a token payload or 41 * an existing subtree. 42 * </summary> 43 * 44 * <remarks> 45 * Once you start next()ing, do not try to add more elements. It will 46 * break the cursor tracking I believe. 47 * 48 * TODO: add mechanism to detect/puke on modification after reading from stream 49 * </remarks> 50 * 51 * <see cref="RewriteRuleSubtreeStream"/> 52 * <see cref="RewriteRuleTokenStream"/> 53 */ 54 [System.Serializable] 55 public abstract class RewriteRuleElementStream { 56 /** <summary> 57 * Cursor 0..n-1. If singleElement!=null, cursor is 0 until you next(), 58 * which bumps it to 1 meaning no more elements. 59 * </summary> 60 */ 61 protected int cursor = 0; 62 63 /** <summary>Track single elements w/o creating a list. Upon 2nd add, alloc list */ 64 protected object singleElement; 65 66 /** <summary>The list of tokens or subtrees we are tracking */ 67 protected IList elements; 68 69 /** <summary>Once a node / subtree has been used in a stream, it must be dup'd 70 * from then on. Streams are reset after subrules so that the streams 71 * can be reused in future subrules. So, reset must set a dirty bit. 72 * If dirty, then next() always returns a dup. 73 * 74 * I wanted to use "naughty bit" here, but couldn't think of a way 75 * to use "naughty". 76 */ 77 protected bool dirty = false; 78 79 /** <summary>The element or stream description; usually has name of the token or 80 * rule reference that this list tracks. Can include rulename too, but 81 * the exception would track that info. 82 */ 83 protected string elementDescription; 84 protected ITreeAdaptor adaptor; 85 86 public RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription) { 87 this.elementDescription = elementDescription; 88 this.adaptor = adaptor; 89 } 90 91 /** <summary>Create a stream with one element</summary> */ 92 public RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription, object oneElement) 93 : this(adaptor, elementDescription) { 94 Add(oneElement); 95 } 96 97 /** <summary>Create a stream, but feed off an existing list</summary> */ 98 public RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription, IList elements) 99 : this(adaptor, elementDescription) { 100 this.singleElement = null; 101 this.elements = elements; 102 } 103 104 /** <summary> 105 * Reset the condition of this stream so that it appears we have 106 * not consumed any of its elements. Elements themselves are untouched. 107 * Once we reset the stream, any future use will need duplicates. Set 108 * the dirty bit. 109 * </summary> 110 */ 111 public virtual void Reset() { 112 cursor = 0; 113 dirty = true; 114 } 115 116 public virtual void Add(object el) { 117 //System.out.println("add '"+elementDescription+"' is "+el); 118 if (el == null) { 119 return; 120 } 121 if (elements != null) { // if in list, just add 122 elements.Add(el); 123 return; 124 } 125 if (singleElement == null) { // no elements yet, track w/o list 126 singleElement = el; 127 return; 128 } 129 // adding 2nd element, move to list 130 elements = new List<object>(5); 131 elements.Add(singleElement); 132 singleElement = null; 133 elements.Add(el); 134 } 135 136 /** <summary> 137 * Return the next element in the stream. If out of elements, throw 138 * an exception unless size()==1. If size is 1, then return elements[0]. 139 * Return a duplicate node/subtree if stream is out of elements and 140 * size==1. If we've already used the element, dup (dirty bit set). 141 * </summary> 142 */ 143 public virtual object NextTree() { 144 int n = Count; 145 if (dirty || (cursor >= n && n == 1)) { 146 // if out of elements and size is 1, dup 147 object el = NextCore(); 148 return Dup(el); 149 } 150 // test size above then fetch 151 object el2 = NextCore(); 152 return el2; 153 } 154 155 /** <summary> 156 * Do the work of getting the next element, making sure that it's 157 * a tree node or subtree. Deal with the optimization of single- 158 * element list versus list of size > 1. Throw an exception 159 * if the stream is empty or we're out of elements and size>1. 160 * protected so you can override in a subclass if necessary. 161 * </summary> 162 */ 163 protected virtual object NextCore() { 164 int n = Count; 165 if (n == 0) { 166 throw new RewriteEmptyStreamException(elementDescription); 167 } 168 if (cursor >= n) { // out of elements? 169 if (n == 1) { // if size is 1, it's ok; return and we'll dup 170 return ToTree(singleElement); 171 } 172 // out of elements and size was not 1, so we can't dup 173 throw new RewriteCardinalityException(elementDescription); 174 } 175 // we have elements 176 if (singleElement != null) { 177 cursor++; // move cursor even for single element list 178 return ToTree(singleElement); 179 } 180 // must have more than one in list, pull from elements 181 object o = ToTree(elements[cursor]); 182 cursor++; 183 return o; 184 } 185 186 /** <summary> 187 * When constructing trees, sometimes we need to dup a token or AST 188 * subtree. Dup'ing a token means just creating another AST node 189 * around it. For trees, you must call the adaptor.dupTree() unless 190 * the element is for a tree root; then it must be a node dup. 191 * </summary> 192 */ 193 protected abstract object Dup(object el); 194 195 /** <summary> 196 * Ensure stream emits trees; tokens must be converted to AST nodes. 197 * AST nodes can be passed through unmolested. 198 * </summary> 199 */ 200 protected virtual object ToTree(object el) { 201 return el; 202 } 203 204 public virtual bool HasNext { 205 get { 206 return (singleElement != null && cursor < 1) || 207 (elements != null && cursor < elements.Count); 208 } 209 } 210 211 public virtual int Count { 212 get { 213 int n = 0; 214 if (singleElement != null) { 215 n = 1; 216 } 217 if (elements != null) { 218 return elements.Count; 219 } 220 return n; 221 } 222 } 223 224 public virtual string Description { 225 get { 226 return elementDescription; 227 } 228 } 229 } 230} 231