Home | History | Annotate | Download | only in Antlr.Runtime.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     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