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