diff --git a/cli/antlr4-4.13.2-SNAPSHOT-complete.jar b/cli/antlr4-4.13.2-SNAPSHOT-complete.jar index f2d5a35..2f670b6 100644 Binary files a/cli/antlr4-4.13.2-SNAPSHOT-complete.jar and b/cli/antlr4-4.13.2-SNAPSHOT-complete.jar differ diff --git a/src/CommonToken.ts b/src/CommonToken.ts index b060379..5f17c50 100644 --- a/src/CommonToken.ts +++ b/src/CommonToken.ts @@ -45,19 +45,17 @@ export class CommonToken implements WritableToken { public type = 0; /** - * This is the backing field for {@link #getLine} and {@link #setLine}. + * The (one-based) line number on which the 1st character of this token was. */ public line = 0; /** - * This is the backing field for {@link #getCharPositionInLine} and - * {@link #setCharPositionInLine}. + * The zero-based index of the first character position in its line. */ public column = -1; // set to invalid position /** - * This is the backing field for {@link #getChannel} and - * {@link #setChannel}. + * The token's channel. */ public channel = Token.DEFAULT_CHANNEL; diff --git a/src/LexerNoViableAltException.ts b/src/LexerNoViableAltException.ts index 7c51dda..3fe53fd 100644 --- a/src/LexerNoViableAltException.ts +++ b/src/LexerNoViableAltException.ts @@ -14,7 +14,7 @@ export class LexerNoViableAltException extends RecognitionException { public startIndex: number; public deadEndConfigs: ATNConfigSet; - public constructor(lexer: Lexer, input: CharStream | TokenStream, startIndex: number, + public constructor(lexer: Lexer | null, input: CharStream | TokenStream, startIndex: number, deadEndConfigs: ATNConfigSet) { super({ message: "", recognizer: lexer, input, ctx: null }); this.startIndex = startIndex; diff --git a/src/RecognitionException.ts b/src/RecognitionException.ts index b8ad296..b6ae6bf 100644 --- a/src/RecognitionException.ts +++ b/src/RecognitionException.ts @@ -51,7 +51,10 @@ export class RecognitionException extends Error { public constructor(params: IExceptionParams) { super(params.message); - if (Error.captureStackTrace) { Error.captureStackTrace(this, RecognitionException); } + if (Error.captureStackTrace) { + Error.captureStackTrace(this, RecognitionException); + } + this.message = params.message; this.recognizer = params.recognizer; this.input = params.input; diff --git a/src/misc/BitSet.ts b/src/misc/BitSet.ts index 57a62a2..08aa998 100644 --- a/src/misc/BitSet.ts +++ b/src/misc/BitSet.ts @@ -5,13 +5,25 @@ */ export class BitSet implements Iterable{ - private data: number[]; + private data: BigUint64Array; - /** Creates a new bit set. All bits are initially `false`. */ - public constructor() { - this.data = []; + /** + * Creates a new bit set. All bits are initially `false`. + * + * @param data Optional initial data. + */ + public constructor(data?: bigint[]) { + if (data) { + // Only use the lower 64 bits. + this.data = new BigUint64Array(data.map((value) => { return BigInt.asUintN(64, value); })); + } else { + this.data = new BigUint64Array(1); + } } + /** + * @returns an iterator over all set bits. + */ public [Symbol.iterator](): IterableIterator { const length = this.data.length; let currentIndex = 0; @@ -24,9 +36,9 @@ export class BitSet implements Iterable{ }, next: (): IteratorResult => { while (currentIndex < length) { - if (currentWord !== 0) { + if (currentWord !== 0n) { const t = currentWord & -currentWord; - const value = (currentIndex << 5) + this.bitCount((t - 1) | 0); + const value = (currentIndex << 6) + this.bitCount(t - 1n); currentWord ^= t; return { done: false, value }; @@ -50,10 +62,10 @@ export class BitSet implements Iterable{ */ public clear(index?: number): void { if (index === undefined) { - this.data = []; + this.data = new BigUint64Array(); } else { this.resize(index); - this.data[index >>> 5] &= ~(1 << index); + this.data[index >>> 6] &= ~BigInt(1 << index); } } @@ -66,7 +78,7 @@ export class BitSet implements Iterable{ */ public or(set: BitSet): void { const minCount = Math.min(this.data.length, set.data.length); - let k = 0 | 0; + let k = 0; for (; k + 7 < minCount; k += 8) { this.data[k] |= set.data[k]; this.data[k + 1] |= set.data[k + 1]; @@ -83,7 +95,7 @@ export class BitSet implements Iterable{ } if (this.data.length < set.data.length) { - this.resize((set.data.length << 5) - 1); + this.resize((set.data.length << 6) - 1); const c = set.data.length; for (let k = minCount; k < c; ++k) { this.data[k] = set.data[k]; @@ -100,7 +112,16 @@ export class BitSet implements Iterable{ * @returns the value of the bit with the specified index. */ public get(index: number): boolean { - return (this.data[index >>> 5] & (1 << index)) !== 0; + if (index < 0) { + throw new RangeError("index cannot be negative"); + } + + const slot = index >>> 6; + if (slot >= this.data.length) { + return false; + } + + return (this.data[slot] & (1n << BigInt(index % 64))) !== 0n; } /** @@ -126,9 +147,9 @@ export class BitSet implements Iterable{ const length = this.data.length; for (let k = 0; k < length; ++k) { let w = this.data[k]; - while (w !== 0) { + while (w !== 0n) { const t = w & -w; - result[pos++] = (k << 5) + this.bitCount((t - 1) | 0); + result[pos++] = (k << 6) + this.bitCount((t - 1n) | 0n); w ^= t; } } @@ -143,6 +164,10 @@ export class BitSet implements Iterable{ * @param fromIndex the index to start checking from (inclusive) */ public nextSetBit(fromIndex: number): number | undefined { + if (fromIndex < 0) { + throw new RangeError("index cannot be negative"); + } + // Iterate over all set bits. for (const index of this) { // Use the first index > than the specified value index. @@ -157,11 +182,15 @@ export class BitSet implements Iterable{ /** * Sets the bit at the specified index to `true`. * - * @param bitIndex a bit index + * @param index a bit index */ - public set(bitIndex: number): void { - this.resize(bitIndex); - this.data[bitIndex >>> 5] |= 1 << bitIndex; + public set(index: number): void { + if (index < 0) { + throw new RangeError("index cannot be negative"); + } + + this.resize(index); + this.data[index >>> 6] |= (1n << BigInt(index % 64)); } /** @@ -172,17 +201,27 @@ export class BitSet implements Iterable{ } private resize(index: number): void { - const count = (index + 32) >>> 5; - for (let i = this.data.length; i < count; i++) { - this.data[i] = 0; + const count = (index + 64) >>> 6; + if (count <= this.data.length) { + return; } + + const data = new BigUint64Array(count); + data.set(this.data); + data.fill(0n, this.data.length); + + this.data = data; }; - private bitCount(v: number): number { // a.k.a. hamming weight - v -= (v >>> 1) & 0x55555555; - v = (v & 0x33333333) + ((v >>> 2) & 0x33333333); + private bitCount(v: bigint): number { // a.k.a. hamming weight + v = v - ((v >> 1n) & 0x5555555555555555n); + v = (v & 0x3333333333333333n) + ((v >> 2n) & 0x3333333333333333n); + v = (v + (v >> 4n)) & 0x0f0f0f0f0f0f0f0fn; + v = v + (v >> 8n); + v = v + (v >> 16n); + v = v + (v >> 32n); - return (((v + (v >>> 4)) & 0xf0f0f0f) * 0x1010101) >>> 24; + return Number(v) & 0x7f; }; } diff --git a/tests/BitSet.spec.ts b/tests/BitSet.spec.ts new file mode 100644 index 0000000..a881db2 --- /dev/null +++ b/tests/BitSet.spec.ts @@ -0,0 +1,121 @@ +/* + * Copyright (c) The ANTLR Project. All rights reserved. + * Use of this file is governed by the BSD 3-clause license that + * can be found in the LICENSE.txt file in the project root. + */ + +import { BitSet } from "../src/misc/BitSet.js"; + +describe("BitSet", () => { + it("Initialize with all bits set to false", () => { + const bitSet = new BitSet(); + expect(bitSet.length).toEqual(0); + }); + + it("Set a single bit to true", () => { + const bitSet = new BitSet(); + bitSet.set(5); + expect(bitSet.get(5)).toEqual(true); + expect(bitSet.length).toEqual(1); + + bitSet.set(1000000); + expect(bitSet.get(1000000)).toEqual(true); + expect(bitSet.get(1000001)).toEqual(false); + expect(bitSet.length).toEqual(2); + }); + + it("Clear a single bit", () => { + const bitSet = new BitSet(); + bitSet.set(5); + bitSet.clear(5); + expect(bitSet.get(5)).toEqual(false); + expect(bitSet.length).toEqual(0); + }); + + it("Clear all bits", () => { + const bitSet = new BitSet(); + bitSet.set(5); + bitSet.set(10); + bitSet.clear(); + expect(bitSet.length).toEqual(0); + }); + + it("Perform logical OR operation with another BitSet", () => { + const bitSet1 = new BitSet(); + bitSet1.set(5); + bitSet1.set(10); + + const bitSet2 = new BitSet(); + bitSet2.set(10); + bitSet2.set(15); + + bitSet1.or(bitSet2); + + expect(bitSet1.get(5)).toEqual(true); + expect(bitSet1.get(10)).toEqual(true); + expect(bitSet1.get(15)).toEqual(true); + expect(bitSet1.length).toEqual(3); + }); + + it("Return the index of the first set bit", () => { + const bitSet = new BitSet(); + bitSet.set(5); + bitSet.set(10); + bitSet.set(15); + + expect(bitSet.nextSetBit(0)).toEqual(5); + expect(bitSet.nextSetBit(6)).toEqual(10); + expect(bitSet.nextSetBit(11)).toEqual(15); + expect(bitSet.nextSetBit(16)).toEqual(undefined); + }); + + it("Return an array with indices of set bits", () => { + const bitSet = new BitSet(); + bitSet.set(5); + bitSet.set(1000); + bitSet.set(15); + + const values = bitSet.values(); + expect(values).toEqual([5, 15, 1000]); + }); + + it("Return a string representation of the BitSet", () => { + const bitSet = new BitSet(); + bitSet.set(5); + bitSet.set(10); + bitSet.set(15); + + expect(bitSet.toString()).toEqual("{5, 10, 15}"); + }); + + it("Create a BitSet with predefined values", () => { + const bitSet = new BitSet([0x003FFE1806000000n]); + expect(bitSet.get(5)).toEqual(false); + expect(bitSet.get(25)).toEqual(true); + expect(bitSet.get(49)).toEqual(true); + expect(bitSet.get(38)).toEqual(false); + expect(bitSet.length).toEqual(17); + }); + + it("Create a BitSet with multiple values", () => { + const bitSet = new BitSet([0x0000000000000001n, 0x003FFE1806000001n]); + expect(bitSet.get(0)).toEqual(true); + expect(bitSet.get(63)).toEqual(false); + expect(bitSet.get(64)).toEqual(true); + expect(bitSet.get(99)).toEqual(true); + expect(bitSet.length).toEqual(19); + }); + + it("Create a BitSet with values exceeding 64 bits", () => { + const bitSet = new BitSet([0x003FFE1806000000n, 0x333333330000000000000001n, 0x4444444123456789ABCDEF0n]); + expect(bitSet.get(5)).toEqual(false); + expect(bitSet.get(47)).toEqual(true); + expect(bitSet.get(64)).toEqual(true); + expect(bitSet.get(133)).toEqual(true); + expect(bitSet.get(188)).toEqual(true); + expect(bitSet.get(2000)).toEqual(false); + expect(bitSet.length).toEqual(50); + }); + + // ... +});