From 466d2f018cc0dd6943a6dfbb5b82b4546482ce8f Mon Sep 17 00:00:00 2001
From: Mike Lischke
Date: Thu, 8 Feb 2024 19:54:11 +0100
Subject: [PATCH] one more
Signed-off-by: Mike Lischke
---
src/atn/ATNConfigSet.ts | 131 ++++++++++++++++++++--------------
src/atn/LexerATNSimulator.ts | 6 +-
src/atn/ParserATNSimulator.ts | 26 +++----
src/atn/PredictionMode.ts | 18 ++---
4 files changed, 104 insertions(+), 77 deletions(-)
diff --git a/src/atn/ATNConfigSet.ts b/src/atn/ATNConfigSet.ts
index a4ba981..9103159 100644
--- a/src/atn/ATNConfigSet.ts
+++ b/src/atn/ATNConfigSet.ts
@@ -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 | null = new HashSet(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
@@ -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(hashATNConfig, equalATNConfigs);
-
- public conflictingAlts: BitSet | null = null;
+ public readonly fullCtx: boolean = false;
/**
* Indicates that the set of configurations is read-only. Do not
@@ -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 {
+ return this.configs[Symbol.iterator]();
}
/**
@@ -100,52 +113,69 @@ export class ATNConfigSet {
* {@link hasSemanticContext} when necessary.
*/
public add(config: ATNConfig,
- mergeCache?: DoubleDict | null): boolean {
- if (mergeCache === undefined) {
- mergeCache = null;
- }
-
+ mergeCache: DoubleDict | 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 {
- const states = new HashSet();
+ /** 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[] {
@@ -159,12 +189,21 @@ export class ATNConfigSet {
return preds;
}
+ public getStates(): HashSet {
+ const states = new HashSet();
+ 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;
}
@@ -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;
@@ -210,6 +249,10 @@ export class ATNConfigSet {
}
}
+ public get length(): number {
+ return this.configs.length;
+ }
+
public isEmpty(): boolean {
return this.configs.length === 0;
}
@@ -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
}
}
@@ -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;
- }
}
diff --git a/src/atn/LexerATNSimulator.ts b/src/atn/LexerATNSimulator.ts
index da8c400..423b52f 100644
--- a/src/atn/LexerATNSimulator.ts
+++ b/src/atn/LexerATNSimulator.ts
@@ -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.
@@ -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;
@@ -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;
diff --git a/src/atn/ParserATNSimulator.ts b/src/atn/ParserATNSimulator.ts
index b67e29b..ac0182e 100644
--- a/src/atn/ParserATNSimulator.ts
+++ b/src/atn/ParserATNSimulator.ts
@@ -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) {
@@ -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) {
@@ -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);
}
@@ -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
@@ -888,7 +888,7 @@ export class ParserATNSimulator extends ATNSimulator {
reach = new ATNConfigSet(fullCtx);
const closureBusy = new HashSet();
const treatEofAsEpsilon = t === Token.EOF;
- for (const config of intermediate.items) {
+ for (const config of intermediate) {
this.closure(config, reach, closureBusy, false, fullCtx, treatEofAsEpsilon);
}
}
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -1123,7 +1123,7 @@ export class ParserATNSimulator extends ATNSimulator {
// From this, it is clear that NONE||anything==NONE.
//
let altToPred: Array | 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);
}
@@ -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;
@@ -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);
@@ -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) {
diff --git a/src/atn/PredictionMode.ts b/src/atn/PredictionMode.ts
index 42ad410..23aa521 100644
--- a/src/atn/PredictionMode.ts
+++ b/src/atn/PredictionMode.ts
@@ -195,7 +195,7 @@ export class PredictionMode {
if (configs.hasSemanticContext) {
// dup configs, tossing out semantic predicates
const dup = new ATNConfigSet();
- for (let c of configs.items) {
+ for (let c of configs) {
c = new ATNConfig({ semanticContext: SemanticContext.NONE }, c);
dup.add(c);
}
@@ -220,7 +220,7 @@ export class PredictionMode {
* {@link RuleStopState}, otherwise {@code false}
*/
public static hasConfigInRuleStopState(configs: ATNConfigSet): boolean {
- for (const c of configs.items) {
+ for (const c of configs) {
if (c.state instanceof RuleStopState) {
return true;
}
@@ -240,7 +240,7 @@ export class PredictionMode {
* {@link RuleStopState}, otherwise {@code false}
*/
public static allConfigsInRuleStopStates(configs: ATNConfigSet): boolean {
- for (const c of configs.items) {
+ for (const c of configs) {
if (!(c.state instanceof RuleStopState)) {
return false;
}
@@ -513,14 +513,14 @@ export class PredictionMode {
},
);
- configs.items.forEach((cfg) => {
+ for (const cfg of configs) {
let alts = configToAlts.get(cfg);
if (alts === null) {
alts = new BitSet();
configToAlts.set(cfg, alts);
}
alts.set(cfg.alt);
- });
+ }
return configToAlts.getValues();
};
@@ -535,14 +535,14 @@ export class PredictionMode {
*/
public static getStateToAltMap(configs: ATNConfigSet): HashMap {
const m = new HashMap();
- configs.items.forEach((c) => {
+ for (const c of configs) {
let alts = m.get(c.state);
if (!alts) {
alts = new BitSet();
m.set(c.state, alts);
}
alts.set(c.alt);
- });
+ }
return m;
};
@@ -550,13 +550,13 @@ export class PredictionMode {
public static hasStateAssociatedWithOneAlt(configs: ATNConfigSet): boolean {
// Count how many alts per state there are in the configs.
const counts: { [key: number]: number; } = {};
- configs.items.forEach((c) => {
+ for (const c of configs) {
const stateNumber = c.state.stateNumber;
if (!counts[stateNumber]) {
counts[stateNumber] = 0;
}
counts[stateNumber]++;
- });
+ }
return Object.values(counts).some((count) => { return count === 1; });
};