Skip to content

Commit

Permalink
one more
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Lischke <[email protected]>
  • Loading branch information
mike-lischke committed Feb 8, 2024
1 parent 4c30abe commit 466d2f0
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 77 deletions.
131 changes: 79 additions & 52 deletions src/atn/ATNConfigSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,25 @@ const equalATNConfigs = (a: ATNConfig, b: ATNConfig): boolean => {
* graph-structured stack
*/
export class ATNConfigSet {
// Track the elements as they are added to the set; supports get(i)///
/**
* The reason that we need this is because we don't want the hash map to use
* the standard hash code and equals. We need all configurations with the
* same
* {@code (s,i,_,semctx)} to be equal. Unfortunately, this key effectively
* doubles
* the number of objects associated with ATNConfigs. The other solution is
* to
* use a hash table that lets us specify the equals/hashCode operation.
* All configs but hashed by (s, i, _, pi) not including context. Wiped out
* when we go readonly as this set becomes a DFA state
*/
public configLookup: HashSet<ATNConfig> | null = new HashSet<ATNConfig>(hashATNConfig, equalATNConfigs);

// Track the elements as they are added to the set; supports get(i).
public configs: ATNConfig[] = [];

public uniqueAlt = 0;

/**
* 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
Expand All @@ -53,25 +69,7 @@ export class ATNConfigSet {
* LL prediction. It will be used to determine how to merge $. With SLL
* it's a wildcard whereas it is not for LL context merge
*/
public readonly fullCtx: boolean;

public uniqueAlt = 0;

/**
* The reason that we need this is because we don't want the hash map to use
* the standard hash code and equals. We need all configurations with the
* same
* {@code (s,i,_,semctx)} to be equal. Unfortunately, this key effectively
* doubles
* the number of objects associated with ATNConfigs. The other solution is
* to
* use a hash table that lets us specify the equals/hashCode operation.
* All configs but hashed by (s, i, _, pi) not including context. Wiped out
* when we go readonly as this set becomes a DFA state
*/
public configLookup = new HashSet<ATNConfig>(hashATNConfig, equalATNConfigs);

public conflictingAlts: BitSet | null = null;
public readonly fullCtx: boolean = false;

/**
* Indicates that the set of configurations is read-only. Do not
Expand All @@ -82,11 +80,26 @@ export class ATNConfigSet {
*/
public readOnly = false;

public conflictingAlts: BitSet | null = null;

private cachedHashCode = -1;

// TODO: add iterator for configs.
public constructor(fullCtx?: boolean) {
this.fullCtx = fullCtx ?? true;
public constructor(fullCtxOrOldSet?: boolean | ATNConfigSet) {
if (fullCtxOrOldSet instanceof ATNConfigSet) {
const old = fullCtxOrOldSet;

this.addAll(old.configs);
this.uniqueAlt = old.uniqueAlt;
this.conflictingAlts = old.conflictingAlts;
this.hasSemanticContext = old.hasSemanticContext;
this.dipsIntoOuterContext = old.dipsIntoOuterContext;
} else {
this.fullCtx = fullCtxOrOldSet ?? true;
}
}

public [Symbol.iterator](): IterableIterator<ATNConfig> {
return this.configs[Symbol.iterator]();
}

/**
Expand All @@ -100,52 +113,69 @@ export class ATNConfigSet {
* {@link hasSemanticContext} when necessary.</p>
*/
public add(config: ATNConfig,
mergeCache?: DoubleDict<PredictionContext, PredictionContext, PredictionContext> | null): boolean {
if (mergeCache === undefined) {
mergeCache = null;
}

mergeCache: DoubleDict<PredictionContext, PredictionContext, PredictionContext> | null = null): boolean {
if (this.readOnly) {
throw new Error("This set is readonly");
}

if (config.semanticContext !== SemanticContext.NONE) {
this.hasSemanticContext = true;
}

if (config.reachesIntoOuterContext > 0) {
this.dipsIntoOuterContext = true;
}
const existing = this.configLookup.add(config);

const existing = this.configLookup!.add(config);
if (existing === config) {
this.cachedHashCode = -1;
this.configs.push(config); // track order here

return true;
}

// a previous (s,i,pi,_), merge with it and save result
const rootIsWildcard = !this.fullCtx;
const merged = merge(existing.context!, config.context!, rootIsWildcard, mergeCache);

/**
* no need to check for existing.context, config.context in cache
* since only way to create new graphs is "call rule" and here. We
* cache at both places
*/
existing.reachesIntoOuterContext = Math.max(existing.reachesIntoOuterContext, config.reachesIntoOuterContext);

// make sure to preserve the precedence filter suppression during the merge
if (config.precedenceFilterSuppressed) {
existing.precedenceFilterSuppressed = true;
}

existing.context = merged; // replace context; no need to alt mapping

return true;
}

public getStates(): HashSet<ATNState> {
const states = new HashSet<ATNState>();
/** Return a List holding list of configs */
public get elements(): ATNConfig[] {
return this.configs;
}

/**
* Gets the complete set of represented alternatives for the configuration set.
*
* @returns the set of represented alternatives in this configuration set
*/
public getAlts(): BitSet {
const alts = new BitSet();
for (const config of this.configs) {
states.add(config.state);
alts.set(config.alt);
}

return states;
return alts;
}

public get(i: number): ATNConfig {
return this.configs[i];
}

public getPredicates(): SemanticContext[] {
Expand All @@ -159,12 +189,21 @@ export class ATNConfigSet {
return preds;
}

public getStates(): HashSet<ATNState> {
const states = new HashSet<ATNState>();
for (const config of this.configs) {
states.add(config.state);
}

return states;
}

public optimizeConfigs(interpreter: ATNSimulator): void {
if (this.readOnly) {
throw new Error("This set is readonly");
}

if (this.configLookup.length === 0) {
if (this.configLookup!.length === 0) {
return;
}

Expand All @@ -175,7 +214,7 @@ export class ATNConfigSet {

public addAll(coll: ATNConfig[]): boolean {
for (const config of coll) {
this.add(config, null);
this.add(config);
}

return false;
Expand Down Expand Up @@ -210,6 +249,10 @@ export class ATNConfigSet {
}
}

public get length(): number {
return this.configs.length;
}

public isEmpty(): boolean {
return this.configs.length === 0;
}
Expand Down Expand Up @@ -242,7 +285,7 @@ export class ATNConfigSet {
public setReadonly(readOnly: boolean): void {
this.readOnly = readOnly;
if (readOnly) {
this.configLookup = new HashSet(); // can't mod, no need for lookup cache
this.configLookup = null; // can't mod, no need for lookup cache
}
}

Expand All @@ -254,20 +297,4 @@ export class ATNConfigSet {
(this.dipsIntoOuterContext ? ",dipsIntoOuterContext" : "");
}

public get items(): ATNConfig[] {
return this.configs;
}

public get length(): number {
return this.configs.length;
}

public getAlts(): BitSet {
const alts = new BitSet();
for (const config of this.configs) {
alts.set(config.alt);
}

return alts;
}
}
6 changes: 3 additions & 3 deletions src/atn/LexerATNSimulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export class LexerATNSimulator extends ATNSimulator {
// Fill reach starting from closure, following t transitions
this.getReachableConfigSet(input, s.configs, reach, t);

if (reach.items.length === 0) { // we got nowhere on t from s
if (reach.length === 0) { // we got nowhere on t from s
if (!reach.hasSemanticContext) {
// we got nowhere on t, don't throw out this knowledge; it'd
// cause a failover from DFA later.
Expand Down Expand Up @@ -330,7 +330,7 @@ export class LexerATNSimulator extends ATNSimulator {
// this is used to skip processing for configs which have a lower priority
// than a config that already reached an accept state for the same rule
let skipAlt = ATN.INVALID_ALT_NUMBER;
for (const cfg of closure.items) {
for (const cfg of closure) {
const currentAltReachedAcceptState = (cfg.alt === skipAlt);
if (currentAltReachedAcceptState && (cfg as LexerATNConfig).passedThroughNonGreedyDecision) {
continue;
Expand Down Expand Up @@ -650,7 +650,7 @@ export class LexerATNSimulator extends ATNSimulator {
protected addDFAState(configs: ATNConfigSet): DFAState {
const proposed = new DFAState(configs);
let firstConfigWithRuleStopState = null;
for (const cfg of configs.items) {
for (const cfg of configs) {
if (cfg.state instanceof RuleStopState) {
firstConfigWithRuleStopState = cfg;
break;
Expand Down
26 changes: 13 additions & 13 deletions src/atn/ParserATNSimulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export class ParserATNSimulator extends ATNSimulator {

protected static getUniqueAlt(configs: ATNConfigSet): number {
let alt = ATN.INVALID_ALT_NUMBER;
for (const c of configs.items) {
for (const c of configs) {
if (alt === ATN.INVALID_ALT_NUMBER) {
alt = c.alt; // found first alt
} else if (c.alt !== alt) {
Expand Down Expand Up @@ -662,7 +662,7 @@ export class ParserATNSimulator extends ATNSimulator {
*/
public dumpDeadEndConfigs(e: NoViableAltException): void {
console.log("dead end configs: ");
const decs = e.deadEndConfigs!.items;
const decs = e.deadEndConfigs!;
for (const c of decs) {
let trans = "no edges";
if (c.state.transitions.length > 0) {
Expand Down Expand Up @@ -828,7 +828,7 @@ export class ParserATNSimulator extends ATNSimulator {
let skippedStopStates = null;

// First figure out where we can reach on input t
for (const c of closure.items) {
for (const c of closure) {
if (ParserATNSimulator.debug) {
console.log("testing " + this.getTokenName(t) + " at " + c);
}
Expand Down Expand Up @@ -869,7 +869,7 @@ export class ParserATNSimulator extends ATNSimulator {
// withheld in skippedStopStates, or when the current symbol is EOF.
//
if (skippedStopStates === null && t !== Token.EOF) {
if (intermediate.items.length === 1) {
if (intermediate.length === 1) {
// Don't pursue the closure if there is just one state.
// It can only have one alternative; just add to result
// Also don't pursue the closure if there is unique alternative
Expand All @@ -888,7 +888,7 @@ export class ParserATNSimulator extends ATNSimulator {
reach = new ATNConfigSet(fullCtx);
const closureBusy = new HashSet<ATNConfig>();
const treatEofAsEpsilon = t === Token.EOF;
for (const config of intermediate.items) {
for (const config of intermediate) {
this.closure(config, reach, closureBusy, false, fullCtx, treatEofAsEpsilon);
}
}
Expand Down Expand Up @@ -931,7 +931,7 @@ export class ParserATNSimulator extends ATNSimulator {
console.log("computeReachSet " + closure + " -> " + reach);
}

if (reach.items.length === 0) {
if (reach.length === 0) {
return null;
} else {
return reach;
Expand Down Expand Up @@ -964,7 +964,7 @@ export class ParserATNSimulator extends ATNSimulator {
}

const result = new ATNConfigSet(configs.fullCtx);
for (const config of configs.items) {
for (const config of configs) {
if (config.state instanceof RuleStopState) {
result.add(config, this.mergeCache);
continue;
Expand Down Expand Up @@ -1061,7 +1061,7 @@ export class ParserATNSimulator extends ATNSimulator {
protected applyPrecedenceFilter(configs: ATNConfigSet): ATNConfigSet {
const statesFromAlt1 = [];
const configSet = new ATNConfigSet(configs.fullCtx);
for (const config of configs.items) {
for (const config of configs) {
// handle alt 1 first
if (config.alt !== 1) {
continue;
Expand All @@ -1079,7 +1079,7 @@ export class ParserATNSimulator extends ATNSimulator {
}
}

for (const config of configs.items) {
for (const config of configs) {
if (config.alt === 1) {
// already handled
continue;
Expand Down Expand Up @@ -1123,7 +1123,7 @@ export class ParserATNSimulator extends ATNSimulator {
// From this, it is clear that NONE||anything==NONE.
//
let altToPred: Array<SemanticContext | null> | null = [];
for (const c of configs.items) {
for (const c of configs) {
if (ambigAlts.get(c.alt)) {
altToPred[c.alt] = SemanticContext.orContext(altToPred[c.alt] ?? null, c.semanticContext);
}
Expand Down Expand Up @@ -1229,7 +1229,7 @@ export class ParserATNSimulator extends ATNSimulator {
}

// Is there a syntactically valid path with a failed pred?
if (semInvalidConfigs.items.length > 0) {
if (semInvalidConfigs.length > 0) {
alt = this.getAltThatFinishedDecisionEntryRule(semInvalidConfigs);
if (alt !== ATN.INVALID_ALT_NUMBER) { // syntactically viable path exists
return alt;
Expand All @@ -1241,7 +1241,7 @@ export class ParserATNSimulator extends ATNSimulator {

protected getAltThatFinishedDecisionEntryRule(configs: ATNConfigSet): number {
const alts = [];
for (const c of configs.items) {
for (const c of configs) {
if (c.reachesIntoOuterContext > 0 || ((c.state instanceof RuleStopState) && c.context!.hasEmptyPath())) {
if (alts.indexOf(c.alt) < 0) {
alts.push(c.alt);
Expand Down Expand Up @@ -1270,7 +1270,7 @@ export class ParserATNSimulator extends ATNSimulator {
const succeeded = new ATNConfigSet(configs.fullCtx);
const failed = new ATNConfigSet(configs.fullCtx);

for (const c of configs.items) {
for (const c of configs) {
if (c.semanticContext !== SemanticContext.NONE) {
const predicateEvaluationResult = c.semanticContext.evaluate(this.parser, outerContext);
if (predicateEvaluationResult) {
Expand Down
Loading

0 comments on commit 466d2f0

Please sign in to comment.