diff options
Diffstat (limited to 'runtime/JavaScript/src/org/antlr/runtime/BitSet.js')
-rwxr-xr-x | runtime/JavaScript/src/org/antlr/runtime/BitSet.js | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/runtime/JavaScript/src/org/antlr/runtime/BitSet.js b/runtime/JavaScript/src/org/antlr/runtime/BitSet.js new file mode 100755 index 0000000..99bfda1 --- /dev/null +++ b/runtime/JavaScript/src/org/antlr/runtime/BitSet.js @@ -0,0 +1,706 @@ +/** + * A BitSet similar to java.util.BitSet. + * + * <p>JavaScript Note: There is no good way to implement something like this in + * JavaScript. JS has no true int type, arrays are usually implemented as + * hashes, etc. This class should probably be nixed for something that is + * similarly (in)efficient, but more clear.</p> + * + * @class + * @param {Number|Array} [bits] a 32 bit number or array of 32 bit numbers + * representing the bitset. These are typically + * generated by the ANTLR Tool. + */ +org.antlr.runtime.BitSet = function(bits) { + if (!bits) { + bits = org.antlr.runtime.BitSet.BITS; + } + + if (org.antlr.lang.isArray(bits)) { + /** + * An array of Numbers representing the BitSet. + * @type Array + */ + this.bits = bits; + } else if(org.antlr.lang.isNumber(bits)) { + this.bits = []; + } +}; + +org.antlr.lang.augmentObject(org.antlr.runtime.BitSet, { + /** + * Number of bits in each number. + * @constant + * @memberOf org.antlr.runtime.BitSet + */ + BITS: 32, + + /** + * Log (base 2) of the number of bits in each number. + * @constant + * @memberOf org.antlr.runtime.BitSet + */ + LOG_BITS: 5, // 2^5 == 32 + + /** + * We will often need to do a mod operator (i mod nbits). Its + * turns out that, for powers of two, this mod operation is + * same as (i & (nbits-1)). Since mod is slow, we use a + * precomputed mod mask to do the mod instead. + * @constant + * @memberOf org.antlr.runtime.BitSet + */ + MOD_MASK: 31, // BITS - 1 + + /** + * Create mask for bit modded to fit in a single word. + * @example + * bitmask(35) => 00000000000000000000000000000100 + * bitmask(3) => 00000000000000000000000000000100 + * @param {Number} bitNumber the bit to create a mask for. + * @returns {Number} the bitmask. + * @memberOf org.antlr.runtime.BitSet + * @private + */ + bitMask: function(bitNumber) { + var bitPosition = bitNumber & org.antlr.runtime.BitSet.MOD_MASK; + return 1 << bitPosition; + }, + + /** + * Calculate the minimum number of bits needed to represent el. + * @param {Number} el a number to be included in the BitSet. + * @returns {Number} the number of bits need to create a BitSet with member + * el. + * @memberOf org.antlr.runtime.BitSet + * @private + */ + numWordsToHold: function(el) { + return (el >> org.antlr.runtime.BitSet.LOG_BITS) + 1; + }, + + /** + * @param {Number} bit a number to be included in the BitSet + * @returns {Number} the index of the word in the field bits that would + * hold bit. + * @memberOf org.antlr.runtime.BitSet + * @private + */ + wordNumber: function(bit) { + return bit >> org.antlr.runtime.BitSet.LOG_BITS; // bit / BITS + }, + + /** + * BitSet factory method. + * + * <p>Operates in a number of modes: + * <ul> + * <li>If el is a number create the BitSet containing that number.</li> + * <li>If el is an array create the BitSet containing each number in the + * array.</li> + * <li>If el is a BitSet return el.</li> + * <li>If el is an Object create the BitSet containing each numeric value + * in el.</li> + * <li>If el is a number and el2 is a number return a BitSet containing + * the numbers between el and el2 (inclusive).</li> + * </ul> + * </p> + * @param {Number|Array|org.antlr.runtime.BitSet|Object} el + * @param {Number} el2 + * @returns {org.antlr.runtime.BitSet} + * @memberOf org.antlr.runtime.BitSet + */ + of: function(el, el2) { + var i, n, s, keys; + + if (org.antlr.lang.isNumber(el)) { + if (org.antlr.lang.isNumber(el2)) { + s = new org.antlr.runtime.BitSet(el2 + 1); + for (i = el; i <= el2; i++) { + n = org.antlr.runtime.BitSet.wordNumber(i); + s.bits[n] |= org.antlr.runtime.BitSet.bitMask(i); + } + return s; + } else { + s = new org.antlr.runtime.BitSet(el + 1); + s.add(el); + return s; + } + } else if(org.antlr.lang.isArray(el)) { + s = new org.antlr.runtime.BitSet(); + for (i=el.length-1; i>=0; i--) { + s.add(el[i]); + } + return s; + } else if (el instanceof org.antlr.runtime.BitSet) { + if (!el) { + return null; + } + return el; + } else if (el instanceof org.antlr.runtime.IntervalSet) { + if (!el) { + return null; + } + s = new org.antlr.runtime.BitSet(); + s.addAll(el); + return s; + } else if (org.antlr.lang.isObject(el)) { + keys = []; + for (i in el) { + if (org.antlr.lang.isNumber(i)) { + keys.push(i); + } + } + return org.antlr.runtime.BitSet.of(keys); + } + } +}); + + + +org.antlr.runtime.BitSet.prototype = { + /** + * Add el into this set. + * @param {Number} el the number to add to the set. + */ + add: function(el) { + var n = org.antlr.runtime.BitSet.wordNumber(el); + if (n >= this.bits.length) { + this.growToInclude(el); + } + this.bits[n] |= org.antlr.runtime.BitSet.bitMask(el); + }, + + /** + * Add multiple elements into this set. + * @param {Array|org.antlr.runtime.BitSet} elements the elements to be added to + * this set. + */ + addAll: function(elements) { + var other, + i, + e; + + if ( elements instanceof org.antlr.runtime.BitSet ) { + this.orInPlace(elements); + } + else if ( elements instanceof org.antlr.runtime.IntervalSet ) { + other = elements; + // walk set and add each interval + /* @todo after implementing intervalset + for (Iterator iter = other.intervals.iterator(); iter.hasNext();) { + Interval I = (Interval) iter.next(); + this.orInPlace(BitSet.range(I.a,I.b)); + }*/ + } else if (org.antlr.lang.isArray(elements)) { + for (i = 0; i < elements.length; i++) { + e = elements[i]; + this.add(e); + } + } else { + return; + } + }, + + /** + * Clone this BitSet and then {@link #andInPlace} with a. + * @param {org.antlr.runtime.BitSet} a a bit set. + * @returns {org.antlr.runtime.BitSet} + */ + and: function(a) { + var s = this.clone(); + s.andInPlace(a); + return s; + }, + + /** + * Perform a logical AND of this target BitSet with the argument BitSet. + * + * This bit set is modified so that each bit in it has the value true if + * and only if it both initially had the value true and the corresponding + * bit in the bit set argument also had the value true. + * @param {org.antlr.runtime.BitSet} a a bit set. + * @returns {org.antlr.runtime.BitSet} + */ + andInPlace: function(a) { + var min = Math.min(this.bits.length, a.bits.length), + i; + for (i = min - 1; i >= 0; i--) { + this.bits[i] &= a.bits[i]; + } + // clear all bits in this not present in a (if this bigger than a). + for (i = min; i < this.bits.length; i++) { + this.bits[i] = 0; + } + }, + + /** + * Clear all bits or a specific bit. + * + * If no arguments given, sets all of the bits in this BitSet to false. + * If one argument given, sets the bit specified by the index to false. + * @param {Number} [el] the index of the bit to be cleared. + */ + clear: function(el) { + if (arguments.length===0) { + var i; + for (i = this.bits.length - 1; i >= 0; i--) { + this.bits[i] = 0; + } + return; + } + + var n = org.antlr.runtime.BitSet.wordNumber(el); + if (n >= this.bits.length) { // grow as necessary to accommodate + this.growToInclude(el); + } + this.bits[n] &= ~org.antlr.runtime.BitSet.bitMask(el); + }, + + /** + * Cloning this BitSet produces a new BitSet that is equal to it. + * + * The clone of the bit set is another bit set that has exactly the same + * bit set to true as this bit set. + * @returns {org.antlr.runtime.BitSet} a clone of this BitSet. + */ + clone: function() { + var i, len, b=[]; + for (i=0, len=this.bits.length; i<len; i++) { + b[i] = this.bits[i]; + } + return new org.antlr.runtime.BitSet(b); + }, + + /** + * Returns the number of bits of space actually in use by this BitSet to + * represent bit values. + * + * The maximum element in the set is the size - 1st element. + * @returns {Number} the number of bits currently in this bit set. + */ + size: function() { + var deg = 0, i, word, bit; + for (i = this.bits.length - 1; i >= 0; i--) { + word = this.bits[i]; + if (word !== 0) { + for (bit = org.antlr.runtime.BitSet.BITS - 1; bit >= 0; bit--) { + if ((word & (1 << bit)) !== 0) { + deg++; + } + } + } + } + return deg; + }, + + /** + * Compares this object against the specified object. + * + * The result is true if and only if the argument is not null and is a + * BitSet object that has exactly the same set of bits set to true as + * this bit set. That is, for every nonnegative int index k, + * <pre><code> + * ((BitSet)obj).get(k) == this.get(k) + * </code></pre> + * must be true. The current sizes of the two bit sets are not compared. + * @param {Object} other the object to compare with. + * @returns {Boolean} if the objects are the same; false otherwise. + */ + equals: function(other) { + if ( !other || !(other instanceof org.antlr.runtime.BitSet) ) { + return false; + } + + var otherSet = other, + i, + n = Math.min(this.bits.length, otherSet.bits.length); + + // for any bits in common, compare + for (i=0; i<n; i++) { + if (this.bits[i] != otherSet.bits[i]) { + return false; + } + } + + // make sure any extra bits are off + + if (this.bits.length > n) { + for (i = n+1; i<this.bits.length; i++) { + if (this.bits[i] !== 0) { + return false; + } + } + } + else if (otherSet.bits.length > n) { + for (i = n+1; i<otherSet.bits.length; i++) { + if (otherSet.bits[i] !== 0) { + return false; + } + } + } + + return true; + }, + + /** + * Grows the set to a larger number of bits. + * @param {Number} bit element that must fit in set + * @private + */ + growToInclude: function(bit) { + var newSize = Math.max(this.bits.length << 1, org.antlr.runtime.BitSet.numWordsToHold(bit)), + newbits = [], //new Array(newSize), + i, + len; + for (i=0, len=this.bits.length; i<len; i++) { + newbits[i] = this.bits[i]; + } + this.bits = newbits; + }, + + /** + * Returns the value of the bit with the specified index. + * + * The value is true if the bit with the index el is currently set + * in this BitSet; otherwise, the result is false. + * @param {Number} el the bit index. + * @returns {Boolean} the value of the bit with the specified index. + */ + member: function(el) { + var n = org.antlr.runtime.BitSet.wordNumber(el); + if (n >= this.bits.length) { return false; } + return (this.bits[n] & org.antlr.runtime.BitSet.bitMask(el)) !== 0; + }, + + /** + * Returns the index of the first bit that is set to true. + * If no such bit exists then -1 is returned. + * @returns {Number} the index of the next set bit. + */ + getSingleElement: function() { + var i; + for (i = 0; i < (this.bits.length << org.antlr.runtime.BitSet.LOG_BITS); i++) { + if (this.member(i)) { + return i; + } + } + return -1; //Label.INVALID; + }, + + /** + * Returns true if this BitSet contains no bits that are set to true. + * @returns {Boolean} boolean indicating whether this BitSet is empty. + */ + isNil: function() { + var i; + for (i = this.bits.length - 1; i >= 0; i--) { + if (this.bits[i] !== 0) { + return false; + } + } + return true; + }, + + /** + * If a bit set argument is passed performs a {@link #subtract} of this bit + * set with the argument bit set. If no argument is passed, clone this bit + * set and {@link #notInPlace}. + * @param {org.antlr.runtime.BitSet} [set] + * @returns {org.antlr.runtime.BitSet} + */ + complement: function(set) { + if (set) { + return set.subtract(this); + } else { + var s = this.clone(); + s.notInPlace(); + return s; + } + }, + + /** + * If no arguments are passed sets all bits to the complement of their + * current values. If one argument is passed sets each bit from the + * beginning of the bit set to index1 (inclusive) to the complement of its + * current value. If two arguments are passed sets each bit from the + * specified index1 (inclusive) to the sepcified index2 (inclusive) to the + * complement of its current value. + * @param {Number} index1 + * @param {Number} index2 + */ + notInPlace: function() { + var minBit, maxBit, i, n; + if (arguments.length===0) { + for (i = this.bits.length - 1; i >= 0; i--) { + this.bits[i] = ~this.bits[i]; + } + } else { + if (arguments.length===1) { + minBit = 0; + maxBit = arguments[0]; + } else { + minBit = arguments[0]; + maxBit = arguments[1]; + } + // make sure that we have room for maxBit + this.growToInclude(maxBit); + for (i = minBit; i <= maxBit; i++) { + n = org.antlr.runtime.BitSet.wordNumber(i); + this.bits[n] ^= org.antlr.runtime.BitSet.bitMask(i); + } + } + + }, + + /** + * Performs a logical OR of this bit set with the bit set argument. + * If no argument is passed, return this bit set. Otherwise a clone of + * this bit set is modified so that a bit in it has the value true if and + * only if it either already had the value true or the corresponding bit + * in the bit set argument has the value true. + * @param {org.antlr.runtime.BitSet} [a] a bit set. + * @returns {org.antlr.runtime.BitSet} + */ + or: function(a) { + if ( !a ) { + return this; + } + var s = this.clone(); + s.orInPlace(a); + return s; + }, + + /** + * Performs a logical {@link #or} in place. + * @param {org.antlr.runtime.BitSet} [a] + * @returns {org.antlr.runtime.BitSet} + */ + orInPlace: function(a) { + if ( !a ) { + return; + } + // If this is smaller than a, grow this first + if (a.bits.length > this.bits.length) { + this.setSize(a.bits.length); + } + var min = Math.min(this.bits.length, a.bits.length), + i; + for (i = min - 1; i >= 0; i--) { + this.bits[i] |= a.bits[i]; + } + }, + + /** + * Sets the bit specified by the index to false. + * @param {Number} bitIndex the index of the bit to be cleared. + */ + remove: function(el) { + var n = org.antlr.runtime.BitSet.wordNumber(el); + if (n >= this.bits.length) { + this.growToInclude(el); + } + this.bits[n] &= ~org.antlr.runtime.BitSet.bitMask(el); + }, + + /** + * Grows the internal bits array to include at least nwords numbers. + * @param {Number} nwords how many words the new set should be + * @private + */ + setSize: function(nwords) { + var n = nwords - this.bits.length; + while (n>=0) { + this.bits.push(0); + n--; + } + }, + + /** + * Returns the number of bits capable of being represented by this bit set + * given its current size. + * @returns {Number} the maximum number of bits that can be represented at + * the moment. + * @private + */ + numBits: function() { + return this.bits.length << org.antlr.runtime.BitSet.LOG_BITS; // num words * bits per word + }, + + /** + * Return how much space is being used by the bits array not + * how many actually have member bits on. + * @returns {Number} the length of the internal bits array. + * @private + */ + lengthInLongWords: function() { + return this.bits.length; + }, + + /** + * Is this bit set contained within a? + * @param {org.antlr.runtime.BitSet} a bit set + * @returns {Boolean} true if and only if a is a subset of this bit set. + */ + subset: function(a) { + if (!a) { return false; } + return this.and(a).equals(this); + }, + + /** + * Subtract the elements of the argument bit set from this bit set in place. + * That is, for each set bit in the argument bit set, set the corresponding + * bit in this bit set to false. + * @param {org.antlr.runtime.BitSet} a bit set. + */ + subtractInPlace: function(a) { + if (!a) { return; } + // for all words of 'a', turn off corresponding bits of 'this' + var i; + for (i = 0; i < this.bits.length && i < a.bits.length; i++) { + this.bits[i] &= ~a.bits[i]; + } + }, + + /** + * Perform a {@link #subtractInPlace} on a clone of this bit set. + * @param {org.antlr.runtime.BitSet} a bit set. + * @returns {org.antlr.runtime.BitSet} the new bit set. + */ + subtract: function(a) { + if (!a || !(a instanceof org.antlr.runtime.BitSet)) { return null; } + + var s = this.clone(); + s.subtractInPlace(a); + return s; + }, + + /* antlr-java needs this to make its class hierarchy happy . . . + toList: function() { + throw new Error("BitSet.toList() unimplemented"); + }, + */ + + /** + * Creates an array of the indexes of each bit set in this bit set. + * @returns {Array} + */ + toArray: function() { + var elems = [], //new Array(this.size()), + i, + en = 0; + for (i = 0; i < (this.bits.length << org.antlr.runtime.BitSet.LOG_BITS); i++) { + if (this.member(i)) { + elems[en++] = i; + } + } + return elems; + }, + + /** + * Returns the internal representation of this bit set. + * This representation is an array of numbers, each representing 32 bits. + * @returns {Array} + */ + toPackedArray: function() { + return this.bits; + }, + + /** + * Returns a string representation of this bit set. + * <p>For every index for which this BitSet contains a bit in the set state, + * the decimal representation of that index is included in the result. + * Such indices are listed in order from lowest to highest, separated by + * ", " (a comma and a space) and surrounded by braces, resulting in the + * usual mathematical notation for a set of integers.</p> + * + * <p>If a grammar g is passed, print g.getTokenDisplayName(i) for each set + * index instead of the numerical index.</p> + * + * <>If two arguments are passed, the first will be used as a custom + * separator string. The second argument is an array whose i-th element + * will be added if the corresponding bit is set.</p> + * + * @param {Object|String} [arg1] an Object with function property + * getTokenDispalyName or a String that will be used as a list + * separator. + * @param {Array} [vocabulary] array from which the i-th value will be + * drawn if the corresponding bit is set. Must pass a string as the + * first argument if using this option. + * @return A commma-separated list of values + */ + toString: function() { + if (arguments.length===0) { + return this.toString1(null); + } else { + if (org.antlr.lang.isString(arguments[0])) { + if (!org.antlr.lang.isValue(arguments[1])) { + return this.toString1(null); + } else { + return this.toString2(arguments[0], arguments[1]); + } + } else { + return this.toString1(arguments[0]); + } + } + }, + + /** + * Transform a bit set into a string by formatting each element as an + * integer separator The string to put in between elements + * @private + * @return A commma-separated list of values + */ + toString1: function(g) { + var buf = "{", + separator = ",", + i, + havePrintedAnElement = false; + + for (i = 0; i < (this.bits.length << org.antlr.runtime.BitSet.LOG_BITS); i++) { + if (this.member(i)) { + if (i > 0 && havePrintedAnElement ) { + buf += separator; + } + if ( g ) { + buf += g.getTokenDisplayName(i); + } + else { + buf += i.toString(); + } + havePrintedAnElement = true; + } + } + return buf + "}"; + }, + + /** + * Create a string representation where instead of integer elements, the + * ith element of vocabulary is displayed instead. Vocabulary is a Vector + * of Strings. + * separator The string to put in between elements + * @private + * @return A commma-separated list of character constants. + */ + toString2: function(separator, vocabulary) { + var str = "", + i; + for (i = 0; i < (this.bits.length << org.antlr.runtime.BitSet.LOG_BITS); i++) { + if (this.member(i)) { + if (str.length > 0) { + str += separator; + } + if (i >= vocabulary.size()) { + str += "'" + i + "'"; + } + else if (!org.antlr.lang.isValue(vocabulary.get(i))) { + str += "'" + i + "'"; + } + else { + str += vocabulary.get(i); + } + } + } + return str; + } +}; |