blob: f92ff28765c80faf6f39557f3ddde227c259972f [file] [log] [blame]
/** A TreeAdaptor that works with any Tree implementation. */
org.antlr.runtime.tree.BaseTreeAdaptor = function() {
this.uniqueNodeID = 1;
};
org.antlr.runtime.tree.TreeAdaptor = function() {};
org.antlr.lang.extend(org.antlr.runtime.tree.BaseTreeAdaptor,
org.antlr.runtime.tree.TreeAdaptor,
{
nil: function() {
return this.create(null);
},
/** create tree node that holds the start and stop tokens associated
* with an error.
*
* If you specify your own kind of tree nodes, you will likely have to
* override this method. CommonTree returns Token.INVALID_TOKEN_TYPE
* if no token payload but you might have to set token type for diff
* node type.
*/
errorNode: function(input, start, stop, e) {
var t = new org.antlr.runtime.tree.CommonErrorNode(input, start, stop, e);
return t;
},
isNil: function(tree) {
return tree.isNil();
},
/** This is generic in the sense that it will work with any kind of
* tree (not just Tree interface). It invokes the adaptor routines
* not the tree node routines to do the construction.
*/
dupTree: function(t, parent) {
if (arguments.length===1) {
parent = null;
}
if ( !t ) {
return null;
}
var newTree = this.dupNode(t);
// ensure new subtree root has parent/child index set
this.setChildIndex(newTree, this.getChildIndex(t)); // same index in new tree
this.setParent(newTree, parent);
var n = this.getChildCount(t),
i, child, newSubTree;
for (i = 0; i < n; i++) {
child = this.getChild(t, i);
newSubTree = this.dupTree(child, t);
this.addChild(newTree, newSubTree);
}
return newTree;
},
/** Add a child to the tree t. If child is a flat tree (a list), make all
* in list children of t. Warning: if t has no children, but child does
* and child isNil then you can decide it is ok to move children to t via
* t.children = child.children; i.e., without copying the array. Just
* make sure that this is consistent with have the user will build
* ASTs.
*/
addChild: function(t, child) {
if ( t && org.antlr.lang.isValue(child) ) {
t.addChild(child);
}
},
/** If oldRoot is a nil root, just copy or move the children to newRoot.
* If not a nil root, make oldRoot a child of newRoot.
*
* old=^(nil a b c), new=r yields ^(r a b c)
* old=^(a b c), new=r yields ^(r ^(a b c))
*
* If newRoot is a nil-rooted single child tree, use the single
* child as the new root node.
*
* old=^(nil a b c), new=^(nil r) yields ^(r a b c)
* old=^(a b c), new=^(nil r) yields ^(r ^(a b c))
*
* If oldRoot was null, it's ok, just return newRoot (even if isNil).
*
* old=null, new=r yields r
* old=null, new=^(nil r) yields ^(nil r)
*
* Return newRoot. Throw an exception if newRoot is not a
* simple node or nil root with a single child node--it must be a root
* node. If newRoot is ^(nil x) return x as newRoot.
*
* Be advised that it's ok for newRoot to point at oldRoot's
* children; i.e., you don't have to copy the list. We are
* constructing these nodes so we should have this control for
* efficiency.
*/
becomeRoot: function(newRoot, oldRoot) {
if (newRoot instanceof org.antlr.runtime.Token || !newRoot) {
newRoot = this.create(newRoot);
}
var newRootTree = newRoot,
oldRootTree = oldRoot;
if ( !oldRoot ) {
return newRoot;
}
// handle ^(nil real-node)
if ( newRootTree.isNil() ) {
var nc = newRootTree.getChildCount();
if (nc===1) {
newRootTree = newRootTree.getChild(0);
} else if ( nc>1 ) {
// TODO: make tree run time exceptions hierarchy
throw new Error("more than one node as root (TODO: make exception hierarchy)");
}
}
// add oldRoot to newRoot; addChild takes care of case where oldRoot
// is a flat list (i.e., nil-rooted tree). All children of oldRoot
// are added to newRoot.
newRootTree.addChild(oldRootTree);
return newRootTree;
},
/** Transform ^(nil x) to x */
rulePostProcessing: function(root) {
var r = root;
if ( r && r.isNil() ) {
if ( r.getChildCount()===0 ) {
r = null;
}
else if ( r.getChildCount()===1 ) {
r = r.getChild(0);
// whoever invokes rule will set parent and child index
r.setParent(null);
r.setChildIndex(-1);
}
}
return r;
},
create: function(tokenType, fromToken) {
var text, t;
if (arguments.length===2) {
if (org.antlr.lang.isString(arguments[1])) {
text = arguments[1];
fromToken = this.createToken(tokenType, text);
t = this.create(fromToken);
return t;
} else {
fromToken = this.createToken(fromToken);
fromToken.setType(tokenType);
t = this.create(fromToken);
return t;
}
} else if (arguments.length===3) {
text = arguments[2];
fromToken = this.createToken(fromToken);
fromToken.setType(tokenType);
fromToken.setText(text);
t = this.create(fromToken);
return t;
}
},
getType: function(t) {
t.getType();
return 0;
},
setType: function(t, type) {
throw new Error("don't know enough about Tree node");
},
getText: function(t) {
return t.getText();
},
setText: function(t, text) {
throw new Error("don't know enough about Tree node");
},
getChild: function(t, i) {
return t.getChild(i);
},
setChild: function(t, i, child) {
t.setChild(i, child);
},
deleteChild: function(t, i) {
return t.deleteChild(i);
},
getChildCount: function(t) {
return t.getChildCount();
},
getUniqueID: function(node) {
if ( !this.treeToUniqueIDMap ) {
this.treeToUniqueIDMap = {};
}
var prevID = this.treeToUniqueIDMap[node];
if ( org.antlr.lang.isValue(prevID) ) {
return prevID;
}
var ID = this.uniqueNodeID;
this.treeToUniqueIDMap[node] = ID;
this.uniqueNodeID++;
return ID;
// GC makes these nonunique:
// return System.identityHashCode(node);
}
});