Home | History | Annotate | Download | only in tree
      1 /** A generic list of elements tracked in an alternative to be used in
      2  *  a -> rewrite rule.  We need to subclass to fill in the next() method,
      3  *  which returns either an AST node wrapped around a token payload or
      4  *  an existing subtree.
      5  *
      6  *  Once you start next()ing, do not try to add more elements.  It will
      7  *  break the cursor tracking I believe.
      8  *
      9  *  @see org.antlr.runtime.tree.RewriteRuleSubtreeStream
     10  *  @see org.antlr.runtime.tree.RewriteRuleTokenStream
     11  *
     12  *  TODO: add mechanism to detect/puke on modification after reading from stream
     13  */
     14 org.antlr.runtime.tree.RewriteRuleElementStream = function(adaptor, elementDescription, el) {
     15     /** Cursor 0..n-1.  If singleElement!=null, cursor is 0 until you next(),
     16      *  which bumps it to 1 meaning no more elements.
     17      */
     18     this.cursor = 0;
     19 
     20     /** Once a node / subtree has been used in a stream, it must be dup'd
     21      *  from then on.  Streams are reset after subrules so that the streams
     22      *  can be reused in future subrules.  So, reset must set a dirty bit.
     23      *  If dirty, then next() always returns a dup.
     24      *
     25      *  I wanted to use "naughty bit" here, but couldn't think of a way
     26      *  to use "naughty".
     27      */
     28     this.dirty = false;
     29 
     30     this.elementDescription = elementDescription;
     31     this.adaptor = adaptor;
     32     if (el) {
     33         if (org.antlr.lang.isArray(el)) {
     34             this.singleElement = null;
     35             this.elements = el;
     36         } else {
     37             this.add(el);
     38         }
     39     }
     40 };
     41 
     42 org.antlr.runtime.tree.RewriteRuleElementStream.prototype = {
     43     /** Reset the condition of this stream so that it appears we have
     44      *  not consumed any of its elements.  Elements themselves are untouched.
     45      *  Once we reset the stream, any future use will need duplicates.  Set
     46      *  the dirty bit.
     47      */
     48     reset: function() {
     49         this.cursor = 0;
     50         this.dirty = true;
     51     },
     52 
     53     add: function(el) {
     54         if ( !org.antlr.lang.isValue(el) ) {
     55             return;
     56         }
     57         if ( this.elements ) { // if in list, just add
     58             this.elements.push(el);
     59             return;
     60         }
     61         if ( !org.antlr.lang.isValue(this.singleElement) ) { // no elements yet, track w/o list
     62             this.singleElement = el;
     63             return;
     64         }
     65         // adding 2nd element, move to list
     66         this.elements = [];
     67         this.elements.push(this.singleElement);
     68         this.singleElement = null;
     69         this.elements.push(el);
     70     },
     71 
     72     /** Return the next element in the stream.  If out of elements, throw
     73      *  an exception unless size()==1.  If size is 1, then return elements[0].
     74      *  Return a duplicate node/subtree if stream is out of elements and
     75      *  size==1.  If we've already used the element, dup (dirty bit set).
     76      */
     77     nextTree: function() {
     78         var n = this.size(),
     79             el;
     80         if ( this.dirty || (this.cursor>=n && n==1) ) {
     81             // if out of elements and size is 1, dup
     82             el = this._next();
     83             return this.dup(el);
     84         }
     85         // test size above then fetch
     86         el = this._next();
     87         return el;
     88     },
     89 
     90     /** do the work of getting the next element, making sure that it's
     91      *  a tree node or subtree.  Deal with the optimization of single-
     92      *  element list versus list of size > 1.  Throw an exception
     93      *  if the stream is empty or we're out of elements and size>1.
     94      *  protected so you can override in a subclass if necessary.
     95      */
     96     _next: function() {
     97         var n = this.size();
     98         if (n===0) {
     99             throw new org.antlr.runtime.tree.RewriteEmptyStreamException(this.elementDescription);
    100         }
    101         if ( this.cursor>= n) { // out of elements?
    102             if ( n===1 ) {  // if size is 1, it's ok; return and we'll dup
    103                 return this.toTree(this.singleElement);
    104             }
    105             // out of elements and size was not 1, so we can't dup
    106             throw new org.antlr.runtime.tree.RewriteCardinalityException(this.elementDescription);
    107         }
    108         // we have elements
    109         if ( org.antlr.lang.isValue(this.singleElement) ) {
    110             this.cursor++; // move cursor even for single element list
    111             return this.toTree(this.singleElement);
    112         }
    113         // must have more than one in list, pull from elements
    114         var o = this.toTree(this.elements[this.cursor]);
    115         this.cursor++;
    116         return o;
    117     },
    118 
    119     /** Ensure stream emits trees; tokens must be converted to AST nodes.
    120      *  AST nodes can be passed through unmolested.
    121      */
    122     toTree: function(el) {
    123         if (el && el.getTree) {
    124             return el.getTree();
    125         }
    126         return el;
    127     },
    128 
    129     hasNext: function() {
    130          return (org.antlr.lang.isValue(this.singleElement) && this.cursor < 1) ||
    131                (this.elements && this.cursor < this.elements.length);
    132     },
    133 
    134     size: function() {
    135         var n = 0;
    136         if ( org.antlr.lang.isValue(this.singleElement) ) {
    137             n = 1;
    138         }
    139         if ( this.elements ) {
    140             return this.elements.length;
    141         }
    142         return n;
    143     },
    144 
    145     getDescription: function() {
    146         return this.elementDescription;
    147     }
    148 };
    149