UNPKG

bitmark-grammar

Version:
542 lines (448 loc) 15.4 kB
/*! * Copyright 2016 The ANTLR Project. All rights reserved. * Licensed under the BSD-3-Clause license. See LICENSE file in the project root for license information. */ // ConvertTo-TS run at 2016-10-04T11:26:25.5488013-07:00 import { Array2DHashMap } from "../misc/Array2DHashMap"; import { Array2DHashSet } from "../misc/Array2DHashSet"; import { ArrayEqualityComparator } from "../misc/ArrayEqualityComparator"; import { ATN } from "./ATN"; import { ATNConfig } from "./ATNConfig"; import { ATNSimulator } from "./ATNSimulator"; import { ATNState } from "./ATNState"; import { BitSet } from "../misc/BitSet"; import { ConflictInfo } from "./ConflictInfo"; import { EqualityComparator } from "../misc/EqualityComparator"; import { JavaSet } from "../misc/Stubs"; import { NotNull, Override } from "../Decorators"; import { ObjectEqualityComparator } from "../misc/ObjectEqualityComparator"; import { PredictionContext } from "./PredictionContext"; import { PredictionContextCache } from "./PredictionContextCache"; import { SemanticContext } from "./SemanticContext"; import * as assert from "assert"; import * as Utils from "../misc/Utils"; interface KeyType { state: number; alt: number; } class KeyTypeEqualityComparer implements EqualityComparator<KeyType> { public hashCode(key: KeyType) { return key.state ^ key.alt; } public equals(a: KeyType, b: KeyType) { return a.state === b.state && a.alt === b.alt; } public static readonly INSTANCE = new KeyTypeEqualityComparer(); } function NewKeyedConfigMap(map?: Array2DHashMap<KeyType, ATNConfig>) { if (map) { return new Array2DHashMap<KeyType, ATNConfig>(map); } else { return new Array2DHashMap<KeyType, ATNConfig>(KeyTypeEqualityComparer.INSTANCE); } } /** * Represents a set of ATN configurations (see `ATNConfig`). As configurations are added to the set, they are merged * with other `ATNConfig` instances already in the set when possible using the graph-structured stack. * * An instance of this class represents the complete set of positions (with context) in an ATN which would be associated * with a single DFA state. Its internal representation is more complex than traditional state used for NFA to DFA * conversion due to performance requirements (both improving speed and reducing memory overhead) as well as supporting * features such as semantic predicates and non-greedy operators in a form to support ANTLR's prediction algorithm. * * @author Sam Harwell */ export class ATNConfigSet implements JavaSet<ATNConfig> { /** * This maps (state, alt) -> merged {@link ATNConfig}. The key does not account for * the {@link ATNConfig#getSemanticContext} of the value, which is only a problem if a single * `ATNConfigSet` contains two configs with the same state and alternative * but different semantic contexts. When this case arises, the first config * added to this map stays, and the remaining configs are placed in {@link #unmerged}. * * This map is only used for optimizing the process of adding configs to the set, * and is `undefined` for read-only sets stored in the DFA. */ private mergedConfigs?: Array2DHashMap<KeyType, ATNConfig>; /** * This is an "overflow" list holding configs which cannot be merged with one * of the configs in {@link #mergedConfigs} but have a colliding key. This * occurs when two configs in the set have the same state and alternative but * different semantic contexts. * * This list is only used for optimizing the process of adding configs to the set, * and is `undefined` for read-only sets stored in the DFA. */ private unmerged?: ATNConfig[]; /** * This is a list of all configs in this set. */ private configs: ATNConfig[]; private _uniqueAlt: number = 0; private _conflictInfo?: ConflictInfo; // Used in parser and lexer. In lexer, it indicates we hit a pred // while computing a closure operation. Don't make a DFA state from this. private _hasSemanticContext: boolean = false; private _dipsIntoOuterContext: boolean = false; /** * When `true`, this config set represents configurations where the entire * outer context has been consumed by the ATN interpreter. This prevents the * {@link ParserATNSimulator#closure} from pursuing the global FOLLOW when a * rule stop state is reached with an empty prediction context. * * Note: `outermostConfigSet` and {@link #dipsIntoOuterContext} should never * be true at the same time. */ private outermostConfigSet: boolean = false; private cachedHashCode: number = -1; constructor(); constructor(set: ATNConfigSet, readonly: boolean); constructor(set?: ATNConfigSet, readonly?: boolean) { if (!set) { this.mergedConfigs = NewKeyedConfigMap(); this.unmerged = []; this.configs = []; this._uniqueAlt = ATN.INVALID_ALT_NUMBER; } else { if (readonly) { this.mergedConfigs = undefined; this.unmerged = undefined; } else if (!set.isReadOnly) { this.mergedConfigs = NewKeyedConfigMap(set.mergedConfigs); this.unmerged = (set.unmerged as ATNConfig[]).slice(0); } else { this.mergedConfigs = NewKeyedConfigMap(); this.unmerged = []; } this.configs = set.configs.slice(0); this._dipsIntoOuterContext = set._dipsIntoOuterContext; this._hasSemanticContext = set._hasSemanticContext; this.outermostConfigSet = set.outermostConfigSet; if (readonly || !set.isReadOnly) { this._uniqueAlt = set._uniqueAlt; this._conflictInfo = set._conflictInfo; } // if (!readonly && set.isReadOnly) -> addAll is called from clone() } } /** * Get the set of all alternatives represented by configurations in this * set. */ @NotNull public getRepresentedAlternatives(): BitSet { if (this._conflictInfo != null) { return this._conflictInfo.conflictedAlts.clone(); } let alts: BitSet = new BitSet(); for (let config of this) { alts.set(config.alt); } return alts; } get isReadOnly(): boolean { return this.mergedConfigs == null; } get isOutermostConfigSet(): boolean { return this.outermostConfigSet; } set isOutermostConfigSet(outermostConfigSet: boolean) { if (this.outermostConfigSet && !outermostConfigSet) { throw new Error("IllegalStateException"); } assert(!outermostConfigSet || !this._dipsIntoOuterContext); this.outermostConfigSet = outermostConfigSet; } public getStates(): Array2DHashSet<ATNState> { let states = new Array2DHashSet<ATNState>(ObjectEqualityComparator.INSTANCE); for (let c of this.configs) { states.add(c.state); } return states; } public optimizeConfigs(interpreter: ATNSimulator): void { if (this.configs.length === 0) { return; } for (let config of this.configs) { config.context = interpreter.atn.getCachedContext(config.context); } } public clone(readonly: boolean): ATNConfigSet { let copy: ATNConfigSet = new ATNConfigSet(this, readonly); if (!readonly && this.isReadOnly) { copy.addAll(this.configs); } return copy; } @Override get size(): number { return this.configs.length; } @Override get isEmpty(): boolean { return this.configs.length === 0; } @Override public contains(o: any): boolean { if (!(o instanceof ATNConfig)) { return false; } if (this.mergedConfigs && this.unmerged) { let config: ATNConfig = o; let configKey = this.getKey(config); let mergedConfig = this.mergedConfigs.get(configKey); if (mergedConfig != null && this.canMerge(config, configKey, mergedConfig)) { return mergedConfig.contains(config); } for (let c of this.unmerged) { if (c.contains(o)) { return true; } } } else { for (let c of this.configs) { if (c.contains(o)) { return true; } } } return false; } @Override public *[Symbol.iterator](): IterableIterator<ATNConfig> { yield* this.configs; } @Override public toArray(): ATNConfig[] { return this.configs; } public add(e: ATNConfig): boolean; public add(e: ATNConfig, contextCache: PredictionContextCache | undefined): boolean; public add(e: ATNConfig, contextCache?: PredictionContextCache): boolean { this.ensureWritable(); if (!this.mergedConfigs || !this.unmerged) { throw new Error("Covered by ensureWritable but duplicated here for strict null check limitation"); } assert(!this.outermostConfigSet || !e.reachesIntoOuterContext); if (contextCache == null) { contextCache = PredictionContextCache.UNCACHED; } let addKey: boolean; let key = this.getKey(e); let mergedConfig = this.mergedConfigs.get(key); addKey = (mergedConfig == null); if (mergedConfig != null && this.canMerge(e, key, mergedConfig)) { mergedConfig.outerContextDepth = Math.max(mergedConfig.outerContextDepth, e.outerContextDepth); if (e.isPrecedenceFilterSuppressed) { mergedConfig.isPrecedenceFilterSuppressed = true; } let joined: PredictionContext = PredictionContext.join(mergedConfig.context, e.context, contextCache); this.updatePropertiesForMergedConfig(e); if (mergedConfig.context === joined) { return false; } mergedConfig.context = joined; return true; } for (let i = 0; i < this.unmerged.length; i++) { let unmergedConfig: ATNConfig = this.unmerged[i]; if (this.canMerge(e, key, unmergedConfig)) { unmergedConfig.outerContextDepth = Math.max(unmergedConfig.outerContextDepth, e.outerContextDepth); if (e.isPrecedenceFilterSuppressed) { unmergedConfig.isPrecedenceFilterSuppressed = true; } let joined: PredictionContext = PredictionContext.join(unmergedConfig.context, e.context, contextCache); this.updatePropertiesForMergedConfig(e); if (unmergedConfig.context === joined) { return false; } unmergedConfig.context = joined; if (addKey) { this.mergedConfigs.put(key, unmergedConfig); this.unmerged.splice(i, 1); } return true; } } this.configs.push(e); if (addKey) { this.mergedConfigs.put(key, e); } else { this.unmerged.push(e); } this.updatePropertiesForAddedConfig(e); return true; } private updatePropertiesForMergedConfig(config: ATNConfig): void { // merged configs can't change the alt or semantic context this._dipsIntoOuterContext = this._dipsIntoOuterContext || config.reachesIntoOuterContext; assert(!this.outermostConfigSet || !this._dipsIntoOuterContext); } private updatePropertiesForAddedConfig(config: ATNConfig): void { if (this.configs.length === 1) { this._uniqueAlt = config.alt; } else if (this._uniqueAlt !== config.alt) { this._uniqueAlt = ATN.INVALID_ALT_NUMBER; } this._hasSemanticContext = this._hasSemanticContext || !SemanticContext.NONE.equals(config.semanticContext); this._dipsIntoOuterContext = this._dipsIntoOuterContext || config.reachesIntoOuterContext; assert(!this.outermostConfigSet || !this._dipsIntoOuterContext); } protected canMerge(left: ATNConfig, leftKey: { state: number, alt: number }, right: ATNConfig): boolean { if (left.state.stateNumber !== right.state.stateNumber) { return false; } if (leftKey.alt !== right.alt) { return false; } return left.semanticContext.equals(right.semanticContext); } protected getKey(e: ATNConfig): { state: number, alt: number } { return { state: e.state.stateNumber, alt: e.alt }; } @Override public containsAll(c: Iterable<any>): boolean { for (let o of c) { if (!(o instanceof ATNConfig)) { return false; } if (!this.contains(o)) { return false; } } return true; } public addAll(c: Iterable<ATNConfig>): boolean; public addAll(c: Iterable<ATNConfig>, contextCache: PredictionContextCache): boolean; public addAll(c: Iterable<ATNConfig>, contextCache?: PredictionContextCache): boolean { this.ensureWritable(); let changed: boolean = false; for (let group of c) { if (this.add(group, contextCache)) { changed = true; } } return changed; } @Override public clear(): void { this.ensureWritable(); if (!this.mergedConfigs || !this.unmerged) { throw new Error("Covered by ensureWritable but duplicated here for strict null check limitation"); } this.mergedConfigs.clear(); this.unmerged.length = 0; this.configs.length = 0; this._dipsIntoOuterContext = false; this._hasSemanticContext = false; this._uniqueAlt = ATN.INVALID_ALT_NUMBER; this._conflictInfo = undefined; } @Override public equals(obj: any): boolean { if (this === obj) { return true; } if (!(obj instanceof ATNConfigSet)) { return false; } return this.outermostConfigSet === obj.outermostConfigSet && Utils.equals(this._conflictInfo, obj._conflictInfo) && ArrayEqualityComparator.INSTANCE.equals(this.configs, obj.configs); } @Override public hashCode(): number { if (this.isReadOnly && this.cachedHashCode !== -1) { return this.cachedHashCode; } let hashCode: number = 1; hashCode = 5 * hashCode ^ (this.outermostConfigSet ? 1 : 0); hashCode = 5 * hashCode ^ ArrayEqualityComparator.INSTANCE.hashCode(this.configs); if (this.isReadOnly) { this.cachedHashCode = hashCode; } return hashCode; } public toString(): string; public toString(showContext: boolean): string; public toString(showContext?: boolean): string { if (showContext == null) { showContext = false; } let buf = ""; let sortedConfigs = this.configs.slice(0); sortedConfigs.sort((o1, o2) => { if (o1.alt !== o2.alt) { return o1.alt - o2.alt; } else if (o1.state.stateNumber !== o2.state.stateNumber) { return o1.state.stateNumber - o2.state.stateNumber; } else { return o1.semanticContext.toString().localeCompare(o2.semanticContext.toString()); } }); buf += ("["); for (let i = 0; i < sortedConfigs.length; i++) { if (i > 0) { buf += (", "); } buf += (sortedConfigs[i].toString(undefined, true, showContext)); } buf += ("]"); if (this._hasSemanticContext) { buf += (",hasSemanticContext=") + (this._hasSemanticContext); } if (this._uniqueAlt !== ATN.INVALID_ALT_NUMBER) { buf += (",uniqueAlt=") + (this._uniqueAlt); } if (this._conflictInfo != null) { buf += (",conflictingAlts=") + (this._conflictInfo.conflictedAlts); if (!this._conflictInfo.isExact) { buf += ("*"); } } if (this._dipsIntoOuterContext) { buf += (",dipsIntoOuterContext"); } return buf.toString(); } get uniqueAlt(): number { return this._uniqueAlt; } get hasSemanticContext(): boolean { return this._hasSemanticContext; } set hasSemanticContext(value: boolean) { this.ensureWritable(); this._hasSemanticContext = value; } get conflictInfo(): ConflictInfo | undefined { return this._conflictInfo; } set conflictInfo(conflictInfo: ConflictInfo | undefined) { this.ensureWritable(); this._conflictInfo = conflictInfo; } get conflictingAlts(): BitSet | undefined { if (this._conflictInfo == null) { return undefined; } return this._conflictInfo.conflictedAlts; } get isExactConflict(): boolean { if (this._conflictInfo == null) { return false; } return this._conflictInfo.isExact; } get dipsIntoOuterContext(): boolean { return this._dipsIntoOuterContext; } public get(index: number): ATNConfig { return this.configs[index]; } protected ensureWritable(): void { if (this.isReadOnly) { throw new Error("This ATNConfigSet is read only."); } } }