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