Home | History | Annotate | Download | only in tree
      1 /** A buffered stream of tree nodes.  Nodes can be from a tree of ANY kind.
      2  *
      3  *  This node stream sucks all nodes out of the tree specified in
      4  *  the constructor during construction and makes pointers into
      5  *  the tree using an array of Object pointers. The stream necessarily
      6  *  includes pointers to DOWN and UP and EOF nodes.
      7  *
      8  *  This stream knows how to mark/release for backtracking.
      9  *
     10  *  This stream is most suitable for tree interpreters that need to
     11  *  jump around a lot or for tree parsers requiring speed (at cost of memory).
     12  *  There is some duplicated functionality here with UnBufferedTreeNodeStream
     13  *  but just in bookkeeping, not tree walking etc...
     14  *
     15  *  @see UnBufferedTreeNodeStream
     16  */
     17 org.antlr.runtime.tree.CommonTreeNodeStream = function(adaptor,
     18                                                     tree,
     19                                                     initialBufferSize)
     20 {
     21     if (arguments.length===1) {
     22         tree = adaptor;
     23         adaptor = new org.antlr.runtime.tree.CommonTreeAdaptor();
     24     }
     25     if (arguments.length <= 2) {
     26         initialBufferSize =
     27             org.antlr.runtime.tree.CommonTreeNodeStream.DEFAULT_INITIAL_BUFFER_SIZE;
     28     }
     29 
     30     /** Reuse same DOWN, UP navigation nodes unless this is true */
     31     this.uniqueNavigationNodes = false;
     32 
     33     /** The index into the nodes list of the current node (next node
     34      *  to consume).  If -1, nodes array not filled yet.
     35      */
     36     this.p = -1;
     37 
     38     var Token = org.antlr.runtime.Token;
     39     this.root = tree;
     40     this.adaptor = adaptor;
     41     this.nodes = []; //new ArrayList(initialBufferSize);
     42     this.down = this.adaptor.create(Token.DOWN, "DOWN");
     43     this.up = this.adaptor.create(Token.UP, "UP");
     44     this.eof = this.adaptor.create(Token.EOF, "EOF");
     45 };
     46 
     47 org.antlr.lang.augmentObject(org.antlr.runtime.tree.CommonTreeNodeStream, {
     48     DEFAULT_INITIAL_BUFFER_SIZE: 100,
     49     INITIAL_CALL_STACK_SIZE: 10
     50 });
     51 
     52 org.antlr.lang.extend(org.antlr.runtime.tree.CommonTreeNodeStream,
     53                   org.antlr.runtime.tree.TreeNodeStream,
     54 {
     55     StreamIterator: function() {
     56         var i = 0,
     57             nodes = this.nodes,
     58             eof = this.eof;
     59 
     60         return {
     61             hasNext: function() {
     62                 return i<nodes.length;
     63             },
     64 
     65             next: function() {
     66                 var current = i;
     67                 i++;
     68                 if ( current < nodes.length ) {
     69                     return nodes[current];
     70                 }
     71                 return eof;
     72             },
     73 
     74             remove: function() {
     75                 throw new Error("cannot remove nodes from stream");
     76             }
     77         };
     78     },
     79 
     80     /** Walk tree with depth-first-search and fill nodes buffer.
     81      *  Don't do DOWN, UP nodes if its a list (t is isNil).
     82      */
     83     fillBuffer: function(t) {
     84         var reset_p = false;
     85         if (org.antlr.lang.isUndefined(t)) {
     86             t = this.root;
     87             reset_p = true;
     88         }
     89 
     90         var nil = this.adaptor.isNil(t);
     91         if ( !nil ) {
     92             this.nodes.push(t); // add this node
     93         }
     94         // add DOWN node if t has children
     95         var n = this.adaptor.getChildCount(t);
     96         if ( !nil && n>0 ) {
     97             this.addNavigationNode(org.antlr.runtime.Token.DOWN);
     98         }
     99         // and now add all its children
    100         var c, child;
    101         for (c=0; c<n; c++) {
    102             child = this.adaptor.getChild(t,c);
    103             this.fillBuffer(child);
    104         }
    105         // add UP node if t has children
    106         if ( !nil && n>0 ) {
    107             this.addNavigationNode(org.antlr.runtime.Token.UP);
    108         }
    109 
    110         if (reset_p) {
    111             this.p = 0; // buffer of nodes intialized now
    112         }
    113     },
    114 
    115     getNodeIndex: function(node) {
    116         if ( this.p==-1 ) {
    117             this.fillBuffer();
    118         }
    119         var i, t;
    120         for (i=0; i<this.nodes.length; i++) {
    121             t = this.nodes[i];
    122             if ( t===node ) {
    123                 return i;
    124             }
    125         }
    126         return -1;
    127     },
    128 
    129     /** As we flatten the tree, we use UP, DOWN nodes to represent
    130      *  the tree structure.  When debugging we need unique nodes
    131      *  so instantiate new ones when uniqueNavigationNodes is true.
    132      */
    133     addNavigationNode: function(ttype) {
    134         var navNode = null;
    135         if ( ttype===org.antlr.runtime.Token.DOWN ) {
    136             if ( this.hasUniqueNavigationNodes() ) {
    137                 navNode = this.adaptor.create(org.antlr.runtime.Token.DOWN, "DOWN");
    138             }
    139             else {
    140                 navNode = this.down;
    141             }
    142         }
    143         else {
    144             if ( this.hasUniqueNavigationNodes() ) {
    145                 navNode = this.adaptor.create(org.antlr.runtime.Token.UP, "UP");
    146             }
    147             else {
    148                 navNode = this.up;
    149             }
    150         }
    151         this.nodes.push(navNode);
    152     },
    153 
    154     get: function(i) {
    155         if ( this.p===-1 ) {
    156             this.fillBuffer();
    157         }
    158         return this.nodes[i];
    159     },
    160 
    161     LT: function(k) {
    162         if ( this.p===-1 ) {
    163             this.fillBuffer();
    164         }
    165         if ( k===0 ) {
    166             return null;
    167         }
    168         if ( k<0 ) {
    169             return this.LB(-1*k);
    170         }
    171         if ( (this.p+k-1) >= this.nodes.length ) {
    172             return this.eof;
    173         }
    174         return this.nodes[this.p+k-1];
    175     },
    176 
    177     getCurrentSymbol: function() { return this.LT(1); },
    178 
    179     /** Look backwards k nodes */
    180     LB: function(k) {
    181         if ( k===0 ) {
    182             return null;
    183         }
    184         if ( (this.p-k)<0 ) {
    185             return null;
    186         }
    187         return this.nodes[this.p-k];
    188     },
    189 
    190     getTreeSource: function() {
    191         return this.root;
    192     },
    193 
    194     getSourceName: function() {
    195         return this.getTokenStream().getSourceName();
    196     },
    197 
    198     getTokenStream: function() {
    199         return this.tokens;
    200     },
    201 
    202     setTokenStream: function(tokens) {
    203         this.tokens = tokens;
    204     },
    205 
    206     getTreeAdaptor: function() {
    207         return this.adaptor;
    208     },
    209 
    210     setTreeAdaptor: function(adaptor) {
    211         this.adaptor = adaptor;
    212     },
    213 
    214     hasUniqueNavigationNodes: function() {
    215         return this.uniqueNavigationNodes;
    216     },
    217 
    218     setUniqueNavigationNodes: function(uniqueNavigationNodes) {
    219         this.uniqueNavigationNodes = uniqueNavigationNodes;
    220     },
    221 
    222     consume: function() {
    223         if ( this.p===-1 ) {
    224             this.fillBuffer();
    225         }
    226         this.p++;
    227     },
    228 
    229     LA: function(i) {
    230         return this.adaptor.getType(this.LT(i));
    231     },
    232 
    233     mark: function() {
    234         if ( this.p===-1 ) {
    235             this.fillBuffer();
    236         }
    237         this.lastMarker = this.index();
    238         return this.lastMarker;
    239     },
    240 
    241     release: function(marker) {
    242         // no resources to release
    243     },
    244 
    245     index: function() {
    246         return this.p;
    247     },
    248 
    249     rewind: function(marker) {
    250         if (!org.antlr.lang.isNumber(marker)) {
    251             marker = this.lastMarker;
    252         }
    253         this.seek(marker);
    254     },
    255 
    256     seek: function(index) {
    257         if ( this.p===-1 ) {
    258             this.fillBuffer();
    259         }
    260         this.p = index;
    261     },
    262 
    263     /** Make stream jump to a new location, saving old location.
    264      *  Switch back with pop().
    265      */
    266     push: function(index) {
    267         if ( !this.calls ) {
    268             this.calls = [];
    269         }
    270         this.calls.push(this.p); // save current index
    271         this.seek(index);
    272     },
    273 
    274     /** Seek back to previous index saved during last push() call.
    275      *  Return top of stack (return index).
    276      */
    277     pop: function() {
    278         var ret = this.calls.pop();
    279         this.seek(ret);
    280         return ret;
    281     },
    282 
    283     reset: function() {
    284         this.p = 0;
    285         this.lastMarker = 0;
    286         if (this.calls) {
    287             this.calls = [];
    288         }
    289     },
    290 
    291     size: function() {
    292         if ( this.p===-1 ) {
    293             this.fillBuffer();
    294         }
    295         return this.nodes.length;
    296     },
    297 
    298     iterator: function() {
    299         if ( this.p===-1 ) {
    300             this.fillBuffer();
    301         }
    302         return this.StreamIterator();
    303     },
    304 
    305     replaceChildren: function(parent, startChildIndex, stopChildIndex, t) {
    306         if ( parent ) {
    307             this.adaptor.replaceChildren(parent, startChildIndex, stopChildIndex, t);
    308         }
    309     },
    310 
    311     /** Debugging */
    312     toTokenString: function(start, stop) {
    313         if ( this.p===-1 ) {
    314             this.fillBuffer();
    315         }
    316         var buf='', i, t;
    317         for (i = start; i < this.nodes.length && i <= stop; i++) {
    318             t = this.nodes[i];
    319             buf += " "+this.adaptor.getToken(t);
    320         }
    321         return buf;
    322     },
    323 
    324     /** Used for testing, just return the token type stream */
    325     toString: function(start, stop) {
    326         var buf = "",
    327             text,
    328             t,
    329             i;
    330         if (arguments.length===0) {
    331             if ( this.p===-1 ) {
    332                 this.fillBuffer();
    333             }
    334             for (i = 0; i < this.nodes.length; i++) {
    335                 t = this.nodes[i];
    336                 buf += " ";
    337                 buf += this.adaptor.getType(t);
    338             }
    339             return buf;
    340         } else {
    341             if ( !org.antlr.lang.isNumber(start) || !org.antlr.lang.isNumber(stop) ) {
    342                 return null;
    343             }
    344             if ( this.p===-1 ) {
    345                 this.fillBuffer();
    346             }
    347             // if we have the token stream, use that to dump text in order
    348             var beginTokenIndex,
    349                 endTokenIndex;
    350             if ( this.tokens ) {
    351                 beginTokenIndex = this.adaptor.getTokenStartIndex(start);
    352                 endTokenIndex = this.adaptor.getTokenStopIndex(stop);
    353                 // if it's a tree, use start/stop index from start node
    354                 // else use token range from start/stop nodes
    355                 if ( this.adaptor.getType(stop)===org.antlr.runtime.Token.UP ) {
    356                     endTokenIndex = this.adaptor.getTokenStopIndex(start);
    357                 }
    358                 else if ( this.adaptor.getType(stop)==org.antlr.runtime.Token.EOF )
    359                 {
    360                     endTokenIndex = this.size()-2; // don't use EOF
    361                 }
    362                 return this.tokens.toString(beginTokenIndex, endTokenIndex);
    363             }
    364             // walk nodes looking for start
    365             t = null;
    366             i = 0;
    367             for (; i < this.nodes.length; i++) {
    368                 t = this.nodes[i];
    369                 if ( t===start ) {
    370                     break;
    371                 }
    372             }
    373             // now walk until we see stop, filling string buffer with text
    374             buf = text = "";
    375             t = this.nodes[i];
    376             while ( t!==stop ) {
    377                 text = this.adaptor.getText(t);
    378                 if ( !org.antlr.lang.isString(text) ) {
    379                     text = " "+this.adaptor.getType(t).toString();
    380                 }
    381                 buf += text;
    382                 i++;
    383                 t = nodes[i];
    384             }
    385             // include stop node too
    386             text = this.adaptor.getText(stop);
    387             if ( !org.antlr.lang.isString(text) ) {
    388                 text = " "+this.adaptor.getType(stop).toString();
    389             }
    390             buf += text;
    391             return buf;
    392         }
    393     }
    394 });
    395