| /** A buffered stream of tree nodes. Nodes can be from a tree of ANY kind. |
| * |
| * This node stream sucks all nodes out of the tree specified in |
| * the constructor during construction and makes pointers into |
| * the tree using an array of Object pointers. The stream necessarily |
| * includes pointers to DOWN and UP and EOF nodes. |
| * |
| * This stream knows how to mark/release for backtracking. |
| * |
| * This stream is most suitable for tree interpreters that need to |
| * jump around a lot or for tree parsers requiring speed (at cost of memory). |
| * There is some duplicated functionality here with UnBufferedTreeNodeStream |
| * but just in bookkeeping, not tree walking etc... |
| * |
| * @see UnBufferedTreeNodeStream |
| */ |
| org.antlr.runtime.tree.CommonTreeNodeStream = function(adaptor, |
| tree, |
| initialBufferSize) |
| { |
| if (arguments.length===1) { |
| tree = adaptor; |
| adaptor = new org.antlr.runtime.tree.CommonTreeAdaptor(); |
| } |
| if (arguments.length <= 2) { |
| initialBufferSize = |
| org.antlr.runtime.tree.CommonTreeNodeStream.DEFAULT_INITIAL_BUFFER_SIZE; |
| } |
| |
| /** Reuse same DOWN, UP navigation nodes unless this is true */ |
| this.uniqueNavigationNodes = false; |
| |
| /** The index into the nodes list of the current node (next node |
| * to consume). If -1, nodes array not filled yet. |
| */ |
| this.p = -1; |
| |
| var Token = org.antlr.runtime.Token; |
| this.root = tree; |
| this.adaptor = adaptor; |
| this.nodes = []; //new ArrayList(initialBufferSize); |
| this.down = this.adaptor.create(Token.DOWN, "DOWN"); |
| this.up = this.adaptor.create(Token.UP, "UP"); |
| this.eof = this.adaptor.create(Token.EOF, "EOF"); |
| }; |
| |
| org.antlr.lang.augmentObject(org.antlr.runtime.tree.CommonTreeNodeStream, { |
| DEFAULT_INITIAL_BUFFER_SIZE: 100, |
| INITIAL_CALL_STACK_SIZE: 10 |
| }); |
| |
| org.antlr.lang.extend(org.antlr.runtime.tree.CommonTreeNodeStream, |
| org.antlr.runtime.tree.TreeNodeStream, |
| { |
| StreamIterator: function() { |
| var i = 0, |
| nodes = this.nodes, |
| eof = this.eof; |
| |
| return { |
| hasNext: function() { |
| return i<nodes.length; |
| }, |
| |
| next: function() { |
| var current = i; |
| i++; |
| if ( current < nodes.length ) { |
| return nodes[current]; |
| } |
| return eof; |
| }, |
| |
| remove: function() { |
| throw new Error("cannot remove nodes from stream"); |
| } |
| }; |
| }, |
| |
| /** Walk tree with depth-first-search and fill nodes buffer. |
| * Don't do DOWN, UP nodes if its a list (t is isNil). |
| */ |
| fillBuffer: function(t) { |
| var reset_p = false; |
| if (org.antlr.lang.isUndefined(t)) { |
| t = this.root; |
| reset_p = true; |
| } |
| |
| var nil = this.adaptor.isNil(t); |
| if ( !nil ) { |
| this.nodes.push(t); // add this node |
| } |
| // add DOWN node if t has children |
| var n = this.adaptor.getChildCount(t); |
| if ( !nil && n>0 ) { |
| this.addNavigationNode(org.antlr.runtime.Token.DOWN); |
| } |
| // and now add all its children |
| var c, child; |
| for (c=0; c<n; c++) { |
| child = this.adaptor.getChild(t,c); |
| this.fillBuffer(child); |
| } |
| // add UP node if t has children |
| if ( !nil && n>0 ) { |
| this.addNavigationNode(org.antlr.runtime.Token.UP); |
| } |
| |
| if (reset_p) { |
| this.p = 0; // buffer of nodes intialized now |
| } |
| }, |
| |
| getNodeIndex: function(node) { |
| if ( this.p==-1 ) { |
| this.fillBuffer(); |
| } |
| var i, t; |
| for (i=0; i<this.nodes.length; i++) { |
| t = this.nodes[i]; |
| if ( t===node ) { |
| return i; |
| } |
| } |
| return -1; |
| }, |
| |
| /** As we flatten the tree, we use UP, DOWN nodes to represent |
| * the tree structure. When debugging we need unique nodes |
| * so instantiate new ones when uniqueNavigationNodes is true. |
| */ |
| addNavigationNode: function(ttype) { |
| var navNode = null; |
| if ( ttype===org.antlr.runtime.Token.DOWN ) { |
| if ( this.hasUniqueNavigationNodes() ) { |
| navNode = this.adaptor.create(org.antlr.runtime.Token.DOWN, "DOWN"); |
| } |
| else { |
| navNode = this.down; |
| } |
| } |
| else { |
| if ( this.hasUniqueNavigationNodes() ) { |
| navNode = this.adaptor.create(org.antlr.runtime.Token.UP, "UP"); |
| } |
| else { |
| navNode = this.up; |
| } |
| } |
| this.nodes.push(navNode); |
| }, |
| |
| get: function(i) { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| return this.nodes[i]; |
| }, |
| |
| LT: function(k) { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| if ( k===0 ) { |
| return null; |
| } |
| if ( k<0 ) { |
| return this.LB(-1*k); |
| } |
| if ( (this.p+k-1) >= this.nodes.length ) { |
| return this.eof; |
| } |
| return this.nodes[this.p+k-1]; |
| }, |
| |
| getCurrentSymbol: function() { return this.LT(1); }, |
| |
| /** Look backwards k nodes */ |
| LB: function(k) { |
| if ( k===0 ) { |
| return null; |
| } |
| if ( (this.p-k)<0 ) { |
| return null; |
| } |
| return this.nodes[this.p-k]; |
| }, |
| |
| getTreeSource: function() { |
| return this.root; |
| }, |
| |
| getSourceName: function() { |
| return this.getTokenStream().getSourceName(); |
| }, |
| |
| getTokenStream: function() { |
| return this.tokens; |
| }, |
| |
| setTokenStream: function(tokens) { |
| this.tokens = tokens; |
| }, |
| |
| getTreeAdaptor: function() { |
| return this.adaptor; |
| }, |
| |
| setTreeAdaptor: function(adaptor) { |
| this.adaptor = adaptor; |
| }, |
| |
| hasUniqueNavigationNodes: function() { |
| return this.uniqueNavigationNodes; |
| }, |
| |
| setUniqueNavigationNodes: function(uniqueNavigationNodes) { |
| this.uniqueNavigationNodes = uniqueNavigationNodes; |
| }, |
| |
| consume: function() { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| this.p++; |
| }, |
| |
| LA: function(i) { |
| return this.adaptor.getType(this.LT(i)); |
| }, |
| |
| mark: function() { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| this.lastMarker = this.index(); |
| return this.lastMarker; |
| }, |
| |
| release: function(marker) { |
| // no resources to release |
| }, |
| |
| index: function() { |
| return this.p; |
| }, |
| |
| rewind: function(marker) { |
| if (!org.antlr.lang.isNumber(marker)) { |
| marker = this.lastMarker; |
| } |
| this.seek(marker); |
| }, |
| |
| seek: function(index) { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| this.p = index; |
| }, |
| |
| /** Make stream jump to a new location, saving old location. |
| * Switch back with pop(). |
| */ |
| push: function(index) { |
| if ( !this.calls ) { |
| this.calls = []; |
| } |
| this.calls.push(this.p); // save current index |
| this.seek(index); |
| }, |
| |
| /** Seek back to previous index saved during last push() call. |
| * Return top of stack (return index). |
| */ |
| pop: function() { |
| var ret = this.calls.pop(); |
| this.seek(ret); |
| return ret; |
| }, |
| |
| reset: function() { |
| this.p = 0; |
| this.lastMarker = 0; |
| if (this.calls) { |
| this.calls = []; |
| } |
| }, |
| |
| size: function() { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| return this.nodes.length; |
| }, |
| |
| iterator: function() { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| return this.StreamIterator(); |
| }, |
| |
| replaceChildren: function(parent, startChildIndex, stopChildIndex, t) { |
| if ( parent ) { |
| this.adaptor.replaceChildren(parent, startChildIndex, stopChildIndex, t); |
| } |
| }, |
| |
| /** Debugging */ |
| toTokenString: function(start, stop) { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| var buf='', i, t; |
| for (i = start; i < this.nodes.length && i <= stop; i++) { |
| t = this.nodes[i]; |
| buf += " "+this.adaptor.getToken(t); |
| } |
| return buf; |
| }, |
| |
| /** Used for testing, just return the token type stream */ |
| toString: function(start, stop) { |
| var buf = "", |
| text, |
| t, |
| i; |
| if (arguments.length===0) { |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| for (i = 0; i < this.nodes.length; i++) { |
| t = this.nodes[i]; |
| buf += " "; |
| buf += this.adaptor.getType(t); |
| } |
| return buf; |
| } else { |
| if ( !org.antlr.lang.isNumber(start) || !org.antlr.lang.isNumber(stop) ) { |
| return null; |
| } |
| if ( this.p===-1 ) { |
| this.fillBuffer(); |
| } |
| // if we have the token stream, use that to dump text in order |
| var beginTokenIndex, |
| endTokenIndex; |
| if ( this.tokens ) { |
| beginTokenIndex = this.adaptor.getTokenStartIndex(start); |
| endTokenIndex = this.adaptor.getTokenStopIndex(stop); |
| // if it's a tree, use start/stop index from start node |
| // else use token range from start/stop nodes |
| if ( this.adaptor.getType(stop)===org.antlr.runtime.Token.UP ) { |
| endTokenIndex = this.adaptor.getTokenStopIndex(start); |
| } |
| else if ( this.adaptor.getType(stop)==org.antlr.runtime.Token.EOF ) |
| { |
| endTokenIndex = this.size()-2; // don't use EOF |
| } |
| return this.tokens.toString(beginTokenIndex, endTokenIndex); |
| } |
| // walk nodes looking for start |
| t = null; |
| i = 0; |
| for (; i < this.nodes.length; i++) { |
| t = this.nodes[i]; |
| if ( t===start ) { |
| break; |
| } |
| } |
| // now walk until we see stop, filling string buffer with text |
| buf = text = ""; |
| t = this.nodes[i]; |
| while ( t!==stop ) { |
| text = this.adaptor.getText(t); |
| if ( !org.antlr.lang.isString(text) ) { |
| text = " "+this.adaptor.getType(t).toString(); |
| } |
| buf += text; |
| i++; |
| t = nodes[i]; |
| } |
| // include stop node too |
| text = this.adaptor.getText(stop); |
| if ( !org.antlr.lang.isString(text) ) { |
| text = " "+this.adaptor.getType(stop).toString(); |
| } |
| buf += text; |
| return buf; |
| } |
| } |
| }); |