UNPKG

antlr4ng

Version:

Alternative JavaScript/TypeScript runtime for ANTLR4

983 lines (966 loc) 26.5 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/atn/ATNSimulator.ts var ATNSimulator_exports = {}; __export(ATNSimulator_exports, { ATNSimulator: () => ATNSimulator }); module.exports = __toCommonJS(ATNSimulator_exports); // src/utils/helpers.ts var valueToString = /* @__PURE__ */ __name((v) => { return v === null ? "null" : v; }, "valueToString"); var arrayToString = /* @__PURE__ */ __name((value) => { return Array.isArray(value) ? "[" + value.map(valueToString).join(", ") + "]" : "null"; }, "arrayToString"); var equalArrays = /* @__PURE__ */ __name((a, b) => { if (a === b) { return true; } if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { const left = a[i]; const right = b[i]; if (left === right) { continue; } if (!left || !left.equals(right)) { return false; } } return true; }, "equalArrays"); var equalNumberArrays = /* @__PURE__ */ __name((a, b) => { if (a === b) { return true; } if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) { return false; } } return true; }, "equalNumberArrays"); // src/dfa/DFAState.ts var DFAState = class _DFAState { static { __name(this, "DFAState"); } stateNumber = -1; configs; /** * `edges[symbol]` points to target of symbol. Shift up by 1 so (-1) {@link Token.EOF} maps to `edges[0]`. */ edges = []; isAcceptState = false; /** * If accept state, what ttype do we match or alt do we predict? This is set to {@link ATN.INVALID_ALT_NUMBER} * when {@link predicates} `!= null` or {@link requiresFullContext}. */ prediction = -1; lexerActionExecutor = null; /** * Indicates that this state was created during SLL prediction that discovered a conflict between the configurations * in the state. Future {@link ParserATNSimulator.execATN} invocations immediately jumped doing * full context prediction if this field is true. */ requiresFullContext = false; /** * During SLL parsing, this is a list of predicates associated with the ATN configurations of the DFA state. * When we have predicates, {@link requiresFullContext} is `false` since full context prediction evaluates * predicates on-the-fly. If this is not null, then {@link prediction} is `ATN.INVALID_ALT_NUMBER`. * * We only use these for non-{@link #requiresFullContext} but conflicting states. That * means we know from the context (it's $ or we don't dip into outer * context) that it's an ambiguity not a conflict. * * This list is computed by {@link ParserATNSimulator#predicateDFAState}. */ predicates = null; constructor(configs) { if (configs) { this.configs = configs; } } static fromState(stateNumber) { const result = new _DFAState(); result.stateNumber = stateNumber; return result; } static fromConfigs(configs) { return new _DFAState(configs); } static hashCode(state) { return state.configs.hashCode(); } /** * Two {@link DFAState} instances are equal if their ATN configuration sets * are the same. This method is used to see if a state already exists. * * Because the number of alternatives and number of ATN configurations are * finite, there is a finite number of DFA states that can be processed. * This is necessary to show that the algorithm terminates. * * Cannot test the DFA state numbers here because in * {@link ParserATNSimulator#addDFAState} we need to know if any other state * exists that has this exact set of ATN configurations. The * {@link #stateNumber} is irrelevant. * * @param a The first {@link DFAState}. * @param b The second {@link DFAState}. * * @returns `true` if the two states are equal, otherwise `false`. */ static equals(a, b) { return a.configs.equals(b.configs); } /** * @returns the set of all alts mentioned by all ATN configurations in this DFA state. */ getAltSet() { const alts = /* @__PURE__ */ new Set(); for (const config of this.configs) { alts.add(config.alt); } if (alts.size === 0) { return null; } return alts; } toString() { let buf = ""; buf += this.stateNumber; buf += ":"; buf += this.configs ? this.configs.toString() : ""; if (this.isAcceptState) { buf += "=>"; if (this.predicates) { buf += arrayToString(this.predicates); } else { buf += this.prediction; } } return buf.toString(); } }; // src/misc/ObjectEqualityComparator.ts var ObjectEqualityComparator = class _ObjectEqualityComparator { static { __name(this, "ObjectEqualityComparator"); } static instance = new _ObjectEqualityComparator(); hashCode(obj) { if (obj == null) { return 0; } return obj.hashCode(); } equals(a, b) { if (a == null) { return b == null; } return a.equals(b); } }; // src/misc/DefaultEqualityComparator.ts var DefaultEqualityComparator = class _DefaultEqualityComparator { static { __name(this, "DefaultEqualityComparator"); } static instance = new _DefaultEqualityComparator(); hashCode(obj) { if (obj == null) { return 0; } return ObjectEqualityComparator.instance.hashCode(obj); } equals(a, b) { if (a == null) { return b == null; } if (typeof a === "string" || typeof a === "number") { return a === b; } return ObjectEqualityComparator.instance.equals(a, b); } }; // src/utils/MurmurHash.ts var c1 = 3432918353; var c2 = 461845907; var r1 = 15; var r2 = 13; var m = 5; var n = 3864292196; var MurmurHash = class _MurmurHash { static { __name(this, "MurmurHash"); } static defaultSeed = 701; constructor() { } /** * Initialize the hash using the specified {@code seed}. * * @param seed the seed * * @returns the intermediate hash value */ static initialize(seed = _MurmurHash.defaultSeed) { return seed; } static updateFromComparable(hash, value) { return this.update(hash, value?.hashCode() ?? 0); } /** * Update the intermediate hash value for the next input {@code value}. * * @param hash The intermediate hash value. * @param value the value to add to the current hash. * * @returns the updated intermediate hash value */ static update(hash, value) { value = Math.imul(value, c1); value = value << r1 | value >>> 32 - r1; value = Math.imul(value, c2); hash = hash ^ value; hash = hash << r2 | hash >>> 32 - r2; hash = Math.imul(hash, m) + n; return hash; } /** * Apply the final computation steps to the intermediate value {@code hash} * to form the final result of the MurmurHash 3 hash function. * * @param hash The intermediate hash value. * @param entryCount The number of values added to the hash. * * @returns the final hash result */ static finish(hash, entryCount) { hash ^= entryCount * 4; hash ^= hash >>> 16; hash = Math.imul(hash, 2246822507); hash ^= hash >>> 13; hash = Math.imul(hash, 3266489909); hash ^= hash >>> 16; return hash; } /** * An all-in-one convenience method to compute a hash for a single value. * * @param value The value to hash. * @param seed The seed for the hash value. * * @returns The computed hash. */ static hashCode(value, seed) { return _MurmurHash.finish(_MurmurHash.update(seed ?? _MurmurHash.defaultSeed, value), 1); } }; // src/misc/HashSet.ts var HashSet = class _HashSet { static { __name(this, "HashSet"); } static defaultLoadFactor = 0.75; static initialCapacity = 16; // must be power of 2 comparator; buckets; threshold; /** How many elements in set */ itemCount = 0; constructor(comparatorOrSet, initialCapacity = _HashSet.initialCapacity) { if (comparatorOrSet instanceof _HashSet) { this.comparator = comparatorOrSet.comparator; this.buckets = comparatorOrSet.buckets.slice(0); for (let i = 0; i < this.buckets.length; i++) { const bucket = this.buckets[i]; if (bucket) { this.buckets[i] = bucket.slice(0); } } this.itemCount = comparatorOrSet.itemCount; this.threshold = comparatorOrSet.threshold; } else { this.comparator = comparatorOrSet ?? DefaultEqualityComparator.instance; this.buckets = this.createBuckets(initialCapacity); this.threshold = Math.floor(_HashSet.initialCapacity * _HashSet.defaultLoadFactor); } } /** * Add `o` to set if not there; return existing value if already * there. This method performs the same operation as {@link #add} aside from * the return value. * * @param o the object to add to the set. * * @returns An existing element that equals to `o` if already in set, otherwise `o`. */ getOrAdd(o) { if (this.itemCount > this.threshold) { this.expand(); } const b = this.getBucket(o); let bucket = this.buckets[b]; if (!bucket) { bucket = [o]; this.buckets[b] = bucket; ++this.itemCount; return o; } for (const existing of bucket) { if (this.comparator.equals(existing, o)) { return existing; } } bucket.push(o); ++this.itemCount; return o; } get(o) { if (o == null) { return o; } const b = this.getBucket(o); const bucket = this.buckets[b]; if (!bucket) { return void 0; } for (const e of bucket) { if (this.comparator.equals(e, o)) { return e; } } return void 0; } /** * Removes the specified element from this set if it is present. * * @param o object to be removed from this set, if present. * * @returns `true` if the set contained the specified element. */ remove(o) { if (o == null) { return false; } const b = this.getBucket(o); const bucket = this.buckets[b]; if (!bucket) { return false; } for (let i = 0; i < bucket.length; i++) { const existing = bucket[i]; if (this.comparator.equals(existing, o)) { bucket.splice(i, 1); --this.itemCount; return true; } } return false; } hashCode() { let hash = MurmurHash.initialize(); for (const bucket of this.buckets) { if (bucket == null) { continue; } for (const o of bucket) { if (o == null) { break; } hash = MurmurHash.update(hash, this.comparator.hashCode(o)); } } hash = MurmurHash.finish(hash, this.size); return hash; } equals(o) { if (o === this) { return true; } if (!(o instanceof _HashSet)) { return false; } if (o.size !== this.size) { return false; } return this.containsAll(o); } add(t) { const existing = this.getOrAdd(t); return existing === t; } contains(o) { return this.containsFast(o); } containsFast(obj) { if (obj == null) { return false; } return this.get(obj) !== void 0; } *[Symbol.iterator]() { yield* this.toArray(); } toArray() { const a = new Array(this.size); let i = 0; for (const bucket of this.buckets) { if (bucket == null) { continue; } for (const o of bucket) { if (o == null) { break; } a[i++] = o; } } return a; } containsAll(collection) { if (collection instanceof _HashSet) { for (const bucket of collection.buckets) { if (bucket == null) { continue; } for (const o of bucket) { if (o == null) { break; } if (!this.containsFast(o)) { return false; } } } } else { for (const o of collection) { if (!this.containsFast(o)) { return false; } } } return true; } addAll(c) { let changed = false; for (const o of c) { const existing = this.getOrAdd(o); if (existing !== o) { changed = true; } } return changed; } clear() { this.buckets = this.createBuckets(_HashSet.initialCapacity); this.itemCount = 0; this.threshold = Math.floor(_HashSet.initialCapacity * _HashSet.defaultLoadFactor); } toString() { if (this.size === 0) { return "{}"; } let buf = "{"; let first = true; for (const bucket of this.buckets) { if (bucket == null) { continue; } for (const o of bucket) { if (o == null) { break; } if (first) { first = false; } else { buf += ", "; } buf += o.toString(); } } buf += "}"; return buf; } toTableString() { let buf = ""; for (const bucket of this.buckets) { if (bucket == null) { buf += "null\n"; continue; } buf += "["; let first = true; for (const o of bucket) { if (first) { first = false; } else { buf += " "; } if (o == null) { buf += "_"; } else { buf += o.toString(); } } buf += "]\n"; } return buf; } getBucket(o) { const hash = this.comparator.hashCode(o); const b = hash & this.buckets.length - 1; return b; } expand() { const old = this.buckets; const newCapacity = this.buckets.length * 2; const newTable = this.createBuckets(newCapacity); this.buckets = newTable; this.threshold = Math.floor(newCapacity * _HashSet.defaultLoadFactor); for (const bucket of old) { if (!bucket) { continue; } for (const o of bucket) { const b = this.getBucket(o); let newBucket = this.buckets[b]; if (!newBucket) { newBucket = []; this.buckets[b] = newBucket; } newBucket.push(o); } } } get size() { return this.itemCount; } get isEmpty() { return this.itemCount === 0; } /** * Return an array of `T[]` with length `capacity`. * * @param capacity the length of the array to return * @returns the newly constructed array */ createBuckets(capacity) { return new Array(capacity); } }; // src/misc/MapKeyEqualityOperator.ts var MapKeyEqualityComparator = class { static { __name(this, "MapKeyEqualityComparator"); } keyComparator; constructor(keyComparator) { this.keyComparator = keyComparator; } hashCode(obj) { return this.keyComparator.hashCode(obj.key); } equals(a, b) { return this.keyComparator.equals(a.key, b.key); } }; // src/misc/HashMap.ts var HashMap = class _HashMap { static { __name(this, "HashMap"); } backingStore; constructor(keyComparer) { if (keyComparer instanceof _HashMap) { this.backingStore = new HashSet(keyComparer.backingStore); } else { keyComparer = keyComparer ?? DefaultEqualityComparator.instance; this.backingStore = new HashSet(new MapKeyEqualityComparator(keyComparer)); } } clear() { this.backingStore.clear(); } containsKey(key) { return this.backingStore.contains({ key }); } get(key) { const bucket = this.backingStore.get({ key }); if (!bucket) { return void 0; } return bucket.value; } get isEmpty() { return this.backingStore.isEmpty; } /** * Sets the value for a key in the map. If the key is not present in the map, it is added. * If the key is present, the value is updated and the old value is returned. * * @param key The key to set. * @param value The value to set. * * @returns The old value for the key, if present. */ set(key, value) { const element = this.backingStore.get({ key, value }); let result; if (!element) { this.backingStore.add({ key, value }); } else { result = element.value; element.value = value; } return result; } /** * Sets the value for a key in the map if the key is not already present. Otherwise the value is not changed and * the old value is returned. * * @param key The key to set. * @param value The value to set. * * @returns The current value for the key, if present. */ setIfAbsent(key, value) { const element = this.backingStore.get({ key, value }); let result; if (!element) { this.backingStore.add({ key, value }); } else { result = element.value; } return result; } keys() { return this.backingStore.toArray().map((bucket) => { return bucket.key; }); } values() { return this.backingStore.toArray().map((bucket) => { return bucket.value; }); } get size() { return this.backingStore.size; } hashCode() { return this.backingStore.hashCode(); } equals(o) { return this.backingStore.equals(o.backingStore); } }; // src/atn/PredictionContext.ts var PredictionContext = class _PredictionContext { static { __name(this, "PredictionContext"); } /** * Represents `$` in an array in full context mode, when `$` * doesn't mean wildcard: `$ + x = [$,x]`. Here, * `$` = {@link EMPTY_RETURN_STATE}. */ static EMPTY_RETURN_STATE = 2147483647; static traceATNSimulator = false; cachedHashCode; constructor(cachedHashCode) { this.cachedHashCode = cachedHashCode; } static calculateEmptyHashCode() { let hash = MurmurHash.initialize(31); hash = MurmurHash.finish(hash, 0); return hash; } static calculateHashCodeSingle(parent, returnState) { let hash = MurmurHash.initialize(31); hash = MurmurHash.updateFromComparable(hash, parent); hash = MurmurHash.update(hash, returnState); hash = MurmurHash.finish(hash, 2); return hash; } static calculateHashCodeList(parents, returnStates) { let hash = MurmurHash.initialize(31); for (const parent of parents) { hash = MurmurHash.updateFromComparable(hash, parent); } for (const returnState of returnStates) { hash = MurmurHash.update(hash, returnState); } hash = MurmurHash.finish(hash, 2 * parents.length); return hash; } isEmpty() { return false; } hasEmptyPath() { return this.getReturnState(this.length - 1) === _PredictionContext.EMPTY_RETURN_STATE; } hashCode() { return this.cachedHashCode; } toString(_recog) { return ""; } }; // src/atn/SingletonPredictionContext.ts var SingletonPredictionContext = class _SingletonPredictionContext extends PredictionContext { static { __name(this, "SingletonPredictionContext"); } parent; returnState; constructor(parent, returnState) { super( parent ? PredictionContext.calculateHashCodeSingle(parent, returnState) : PredictionContext.calculateEmptyHashCode() ); this.parent = parent ?? null; this.returnState = returnState; } getParent(_index) { return this.parent; } getReturnState(_index) { return this.returnState; } equals(other) { if (this === other) { return true; } if (!(other instanceof _SingletonPredictionContext)) { return false; } if (this.hashCode() !== other.hashCode()) { return false; } if (this.returnState !== other.returnState) { return false; } if (this.parent == null) { return other.parent == null; } return this.parent.equals(other.parent); } toString() { const up = this.parent === null ? "" : this.parent.toString(); if (up.length === 0) { if (this.returnState === PredictionContext.EMPTY_RETURN_STATE) { return "$"; } return "" + this.returnState; } else { return "" + this.returnState + " " + up; } } get length() { return 1; } }; // src/atn/EmptyPredictionContext.ts var EmptyPredictionContext = class _EmptyPredictionContext extends SingletonPredictionContext { static { __name(this, "EmptyPredictionContext"); } /** * Represents `$` in local context prediction, which means wildcard. * `*+x = *`. */ static instance = new _EmptyPredictionContext(); constructor() { super(void 0, PredictionContext.EMPTY_RETURN_STATE); } isEmpty() { return true; } getParent() { return null; } getReturnState() { return this.returnState; } equals(other) { return this === other; } toString() { return "$"; } }; // src/atn/helpers.ts var createSingletonPredictionContext = /* @__PURE__ */ __name((parent, returnState) => { if (returnState === PredictionContext.EMPTY_RETURN_STATE && parent === null) { return EmptyPredictionContext.instance; } else { return new SingletonPredictionContext(parent, returnState); } }, "createSingletonPredictionContext"); // src/atn/ArrayPredictionContext.ts var ArrayPredictionContext = class _ArrayPredictionContext extends PredictionContext { static { __name(this, "ArrayPredictionContext"); } parents = []; returnStates = []; constructor(parents, returnStates) { super(PredictionContext.calculateHashCodeList(parents, returnStates)); this.parents = parents; this.returnStates = returnStates; return this; } isEmpty() { return this.returnStates[0] === PredictionContext.EMPTY_RETURN_STATE; } get length() { return this.returnStates.length; } getParent(index) { return this.parents[index]; } getReturnState(index) { return this.returnStates[index]; } equals(other) { if (this === other) { return true; } if (!(other instanceof _ArrayPredictionContext) || this.hashCode() !== other.hashCode()) { return false; } return equalNumberArrays(this.returnStates, other.returnStates) && equalArrays(this.parents, other.parents); } toString() { if (this.isEmpty()) { return "[]"; } const entries = []; for (let i = 0; i < this.returnStates.length; i++) { if (this.returnStates[i] === PredictionContext.EMPTY_RETURN_STATE) { entries.push("$"); continue; } entries.push(this.returnStates[i].toString()); if (this.parents[i]) { entries.push(this.parents[i].toString()); } else { entries.push("null"); } } return `[${entries.join(", ")}]`; } }; // src/atn/PredictionContextUtils.ts var getCachedPredictionContext = /* @__PURE__ */ __name((context, contextCache, visited) => { if (context.isEmpty()) { return context; } let existing = visited.get(context); if (existing) { return existing; } existing = contextCache.get(context); if (existing) { visited.set(context, existing); return existing; } let changed = false; let parents = []; for (let i = 0; i < parents.length; i++) { const parent = getCachedPredictionContext(context.getParent(i), contextCache, visited); if (changed || parent !== context.getParent(i)) { if (!changed) { parents = []; for (let j = 0; j < context.length; j++) { parents[j] = context.getParent(j); } changed = true; } parents[i] = parent; } } if (!changed) { contextCache.add(context); visited.set(context, context); return context; } let updated; if (parents.length === 0) { updated = EmptyPredictionContext.instance; } else if (parents.length === 1) { updated = createSingletonPredictionContext(parents[0] ?? void 0, context.getReturnState(0)); } else { updated = new ArrayPredictionContext(parents, context.returnStates); } contextCache.add(updated); visited.set(updated, updated); visited.set(context, updated); return updated; }, "getCachedPredictionContext"); // src/atn/ATNSimulator.ts var ATNSimulator = class { static { __name(this, "ATNSimulator"); } /** Must distinguish between missing edge and edge we know leads nowhere */ static ERROR = DFAState.fromState(2147483647); atn; /** * The context cache maps all PredictionContext objects that are == * to a single cached copy. This cache is shared across all contexts * in all ATNConfigs in all DFA states. We rebuild each ATNConfigSet * to use only cached nodes/graphs in addDFAState(). We don't want to * fill this during closure() since there are lots of contexts that * pop up but are not used ever again. It also greatly slows down closure(). * * This cache makes a huge difference in memory and a little bit in speed. * For the Java grammar on java.*, it dropped the memory requirements * at the end from 25M to 16M. We don't store any of the full context * graphs in the DFA because they are limited to local context only, * but apparently there's a lot of repetition there as well. We optimize * the config contexts before storing the config set in the DFA states * by literally rebuilding them with cached subgraphs only. * * I tried a cache for use during closure operations, that was * whacked after each adaptivePredict(). It cost a little bit * more time I think and doesn't save on the overall footprint * so it's not worth the complexity. */ sharedContextCache; constructor(atn, sharedContextCache) { this.atn = atn; this.sharedContextCache = sharedContextCache; return this; } getCachedContext(context) { if (!this.sharedContextCache) { return context; } const visited = new HashMap(ObjectEqualityComparator.instance); return getCachedPredictionContext(context, this.sharedContextCache, visited); } };