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 */ 14org.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 42org.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