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 
33 namespace 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 
RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription)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> */
RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription, object oneElement)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> */
RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription, IList elements)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          */
Reset()111         public virtual void Reset() {
112             cursor = 0;
113             dirty = true;
114         }
115 
Add(object el)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          */
NextTree()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          */
NextCore()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          */
Dup(object el)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          */
ToTree(object el)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