blob: d15ff31d9b43067469be0b354ebce884c893ba3e [file] [log] [blame]
/** 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;
}
}
});