Home | History | Annotate | Download | only in Tree
      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.</summary>
     75          */
     76         protected bool dirty = false;
     77 
     78         /** <summary>The element or stream description; usually has name of the token or
     79          *  rule reference that this list tracks.  Can include rulename too, but
     80          *  the exception would track that info.
     81          */
     82         protected string elementDescription;
     83         protected ITreeAdaptor adaptor;
     84 
     85         public RewriteRuleElementStream( ITreeAdaptor adaptor, string elementDescription )
     86         {
     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         {
     95             Add( oneElement );
     96         }
     97 
     98         /** <summary>Create a stream, but feed off an existing list</summary> */
     99         public RewriteRuleElementStream( ITreeAdaptor adaptor, string elementDescription, IList elements )
    100             : this( adaptor, elementDescription )
    101         {
    102             this.singleElement = null;
    103             this.elements = elements;
    104         }
    105 
    106         /** <summary>
    107          *  Reset the condition of this stream so that it appears we have
    108          *  not consumed any of its elements.  Elements themselves are untouched.
    109          *  Once we reset the stream, any future use will need duplicates.  Set
    110          *  the dirty bit.
    111          *  </summary>
    112          */
    113         public virtual void Reset()
    114         {
    115             cursor = 0;
    116             dirty = true;
    117         }
    118 
    119         public virtual void Add( object el )
    120         {
    121             //System.out.println("add '"+elementDescription+"' is "+el);
    122             if ( el == null )
    123             {
    124                 return;
    125             }
    126             if ( elements != null )
    127             { // if in list, just add
    128                 elements.Add( el );
    129                 return;
    130             }
    131             if ( singleElement == null )
    132             { // no elements yet, track w/o list
    133                 singleElement = el;
    134                 return;
    135             }
    136             // adding 2nd element, move to list
    137             elements = new List<object>( 5 );
    138             elements.Add( singleElement );
    139             singleElement = null;
    140             elements.Add( el );
    141         }
    142 
    143         /** <summary>
    144          *  Return the next element in the stream.  If out of elements, throw
    145          *  an exception unless size()==1.  If size is 1, then return elements[0].
    146          *  Return a duplicate node/subtree if stream is out of elements and
    147          *  size==1.  If we've already used the element, dup (dirty bit set).
    148          *  </summary>
    149          */
    150         public virtual object NextTree()
    151         {
    152             int n = Count;
    153             if ( dirty || ( cursor >= n && n == 1 ) )
    154             {
    155                 // if out of elements and size is 1, dup
    156                 object el = NextCore();
    157                 return Dup( el );
    158             }
    159             // test size above then fetch
    160             object el2 = NextCore();
    161             return el2;
    162         }
    163 
    164         /** <summary>
    165          *  Do the work of getting the next element, making sure that it's
    166          *  a tree node or subtree.  Deal with the optimization of single-
    167          *  element list versus list of size > 1.  Throw an exception
    168          *  if the stream is empty or we're out of elements and size>1.
    169          *  protected so you can override in a subclass if necessary.
    170          *  </summary>
    171          */
    172         protected virtual object NextCore()
    173         {
    174             int n = Count;
    175             if ( n == 0 )
    176             {
    177                 throw new RewriteEmptyStreamException( elementDescription );
    178             }
    179             if ( cursor >= n )
    180             { // out of elements?
    181                 if ( n == 1 )
    182                 {  // if size is 1, it's ok; return and we'll dup
    183                     return ToTree( singleElement );
    184                 }
    185                 // out of elements and size was not 1, so we can't dup
    186                 throw new RewriteCardinalityException( elementDescription );
    187             }
    188             // we have elements
    189             if ( singleElement != null )
    190             {
    191                 cursor++; // move cursor even for single element list
    192                 return ToTree( singleElement );
    193             }
    194             // must have more than one in list, pull from elements
    195             object o = ToTree( elements[cursor] );
    196             cursor++;
    197             return o;
    198         }
    199 
    200         /** <summary>
    201          *  When constructing trees, sometimes we need to dup a token or AST
    202          * 	subtree.  Dup'ing a token means just creating another AST node
    203          *  around it.  For trees, you must call the adaptor.dupTree() unless
    204          *  the element is for a tree root; then it must be a node dup.
    205          *  </summary>
    206          */
    207         protected abstract object Dup( object el );
    208 
    209         /** <summary>
    210          *  Ensure stream emits trees; tokens must be converted to AST nodes.
    211          *  AST nodes can be passed through unmolested.
    212          *  </summary>
    213          */
    214         protected virtual object ToTree( object el )
    215         {
    216             return el;
    217         }
    218 
    219         public virtual bool HasNext
    220         {
    221             get
    222             {
    223                 return ( singleElement != null && cursor < 1 ) ||
    224                       ( elements != null && cursor < elements.Count );
    225             }
    226         }
    227 
    228         public virtual int Count
    229         {
    230             get
    231             {
    232                 int n = 0;
    233                 if ( singleElement != null )
    234                 {
    235                     n = 1;
    236                 }
    237                 if ( elements != null )
    238                 {
    239                     return elements.Count;
    240                 }
    241                 return n;
    242             }
    243         }
    244 
    245         public virtual string Description
    246         {
    247             get
    248             {
    249                 return elementDescription;
    250             }
    251         }
    252     }
    253 }
    254