diff --git a/src/atn/ATNConfig.ts b/src/atn/ATNConfig.ts index 6b26320..ad313bd 100644 --- a/src/atn/ATNConfig.ts +++ b/src/atn/ATNConfig.ts @@ -13,24 +13,6 @@ import { Recognizer } from "../Recognizer.js"; import { ATNSimulator } from "./ATNSimulator.js"; import { MurmurHash } from "../utils/MurmurHash.js"; -export interface IATNConfigParameters { - state?: ATNState | null, - alt?: number | null, - context?: PredictionContext | null, - semanticContext?: SemanticContext | null, - reachesIntoOuterContext?: number | null, - precedenceFilterSuppressed?: number, -}; - -export interface ICheckedConfigParameters { - state: ATNState | null, - alt: number | null, - context: PredictionContext | null, - semanticContext: SemanticContext | null, - reachesIntoOuterContext: number | null, - precedenceFilterSuppressed?: boolean, -}; - export class ATNConfig { /** The ATN state associated with this configuration */ public readonly state: ATNState; @@ -46,10 +28,9 @@ export class ATNConfig { * invokes the ATN simulator. * * closure() tracks the depth of how far we dip into the outer context: - * depth > 0. Note that it may not be totally accurate depth since I - * don't ever decrement. TODO: make it a boolean then + * depth > 0. */ - public reachesIntoOuterContext: number = 0; // Not used in hash code. + public reachesIntoOuterContext: boolean = false; // Not used in hash code. public precedenceFilterSuppressed = false; // Not used in hash code. @@ -77,9 +58,7 @@ export class ATNConfig { this.alt = c.alt!; this.context = context; this.#semanticContext = semanticContext ?? SemanticContext.NONE; - if (c.reachesIntoOuterContext !== undefined) { - this.reachesIntoOuterContext = c.reachesIntoOuterContext; - } + this.reachesIntoOuterContext = c.reachesIntoOuterContext!; if (c.precedenceFilterSuppressed !== undefined) { this.precedenceFilterSuppressed = c.precedenceFilterSuppressed; @@ -157,12 +136,8 @@ export class ATNConfig { return "(" + this.state + alt + (this.context !== null ? ",[" + this.context.toString() + "]" : "") + - (this.semanticContext !== SemanticContext.NONE ? - ("," + this.semanticContext.toString()) - : "") + - (this.reachesIntoOuterContext > 0 ? - (",up=" + this.reachesIntoOuterContext) - : "") + ")"; + (this.semanticContext !== SemanticContext.NONE ? ("," + this.semanticContext.toString()) : "") + + (this.reachesIntoOuterContext ? (",up=" + this.reachesIntoOuterContext) : "") + ")"; } } diff --git a/src/atn/ATNConfigSet.ts b/src/atn/ATNConfigSet.ts index a7b4ef8..746711d 100644 --- a/src/atn/ATNConfigSet.ts +++ b/src/atn/ATNConfigSet.ts @@ -126,16 +126,14 @@ export class ATNConfigSet { /** * Adding a new config means merging contexts with existing configs for - * `(s, i, pi, _)`, where `s` is the - * {@link ATNConfig//state}, `i` is the {@link ATNConfig//alt}, and - * `pi` is the {@link ATNConfig//semanticContext}. We use - * `(s,i,pi)` as key. + * `(s, i, pi, _)`, where `s` is the {@link ATNConfig.state}, `i` is the {@link ATNConfig.alt}, and + * `pi` is the {@link ATNConfig.semanticContext}. We use `(s,i,pi)` as key. * * This method updates {@link dipsIntoOuterContext} and * {@link hasSemanticContext} when necessary. */ public add(config: ATNConfig, - mergeCache: DoubleDict | null = null): boolean { + mergeCache: DoubleDict | null = null): void { if (this.readOnly) { throw new Error("This set is readonly"); } @@ -144,20 +142,15 @@ export class ATNConfigSet { this.firstStopState = config; } - if (config.semanticContext !== SemanticContext.NONE) { - this.hasSemanticContext = true; - } - - if (config.reachesIntoOuterContext > 0) { - this.dipsIntoOuterContext = true; - } + this.hasSemanticContext ||= config.semanticContext !== SemanticContext.NONE; + this.dipsIntoOuterContext ||= config.reachesIntoOuterContext; const existing = this.configLookup!.getOrAdd(config); if (existing === config) { this.#cachedHashCode = -1; this.configs.push(config); // track order here - return true; + return; } // a previous (s,i,pi,_), merge with it and save result @@ -169,16 +162,12 @@ export class ATNConfigSet { * 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); + existing.reachesIntoOuterContext ||= config.reachesIntoOuterContext; // make sure to preserve the precedence filter suppression during the merge - if (config.precedenceFilterSuppressed) { - existing.precedenceFilterSuppressed = true; - } + existing.precedenceFilterSuppressed ||= config.precedenceFilterSuppressed; existing.context = merged; // replace context; no need to alt mapping - - return true; } /** Return a List holding list of configs */ @@ -261,15 +250,11 @@ export class ATNConfigSet { } public hashCode(): number { - if (this.readOnly) { - if (this.#cachedHashCode === -1) { - this.#cachedHashCode = this.computeHashCode(); - } - - return this.#cachedHashCode; + if (this.#cachedHashCode === -1) { + this.#cachedHashCode = this.computeHashCode(); } - return this.computeHashCode(); + return this.#cachedHashCode; } public get length(): number { diff --git a/src/atn/AmbiguityInfo.ts b/src/atn/AmbiguityInfo.ts index 193b7ec..161830b 100644 --- a/src/atn/AmbiguityInfo.ts +++ b/src/atn/AmbiguityInfo.ts @@ -18,7 +18,7 @@ import { DecisionEventInfo } from "./DecisionEventInfo.js"; * determine that the SLL conflict is truly an ambiguity. For example, if none * of the ATN configurations in the conflicting SLL configuration set have * traversed a global follow transition (i.e. - * {@link ATNConfig#reachesIntoOuterContext} is 0 for all configurations), then + * {@link ATNConfig.reachesIntoOuterContext} is 0 for all configurations), then * the result of SLL prediction for that input is known to be equivalent to the * result of LL prediction for that input. * @@ -32,7 +32,6 @@ import { DecisionEventInfo } from "./DecisionEventInfo.js"; * @see ParserATNSimulator#reportAmbiguity * @see ANTLRErrorListener#reportAmbiguity */ - export interface AmbiguityInfo extends DecisionEventInfo { /** The set of alternative numbers for this decision event that lead to a valid parse. */ ambigAlts: BitSet | null; diff --git a/src/atn/LexerATNSimulator.ts b/src/atn/LexerATNSimulator.ts index 563946a..49b2e37 100644 --- a/src/atn/LexerATNSimulator.ts +++ b/src/atn/LexerATNSimulator.ts @@ -7,7 +7,7 @@ /* eslint-disable jsdoc/require-param, jsdoc/require-returns */ import { Token } from "../Token.js"; -import { Lexer } from "../Lexer.js"; +import { Lexer, type LexerOptions } from "../Lexer.js"; import { ATN } from "./ATN.js"; import { ATNSimulator } from "./ATNSimulator.js"; import { DFAState } from "../dfa/DFAState.js"; @@ -53,9 +53,6 @@ interface SimState { } export class LexerATNSimulator extends ATNSimulator { - // ml: why's that configurable? Memory consumption? - public static readonly MAX_DFA_EDGE = 256; // forces unicode to stay in ATN - public readonly decisionToDFA: DFA[]; public readonly recognizer: Lexer | null = null; @@ -79,8 +76,7 @@ export class LexerATNSimulator extends ATNSimulator { /** Used during DFA/ATN exec to record the most recent accept configuration info */ #prevAccept: SimState | undefined; - #minCodePoint: number; - #maxCodePoint: number; + #options: LexerOptions; /** Lookup table for lexer ATN config creation. */ #lexerATNConfigFactory: Array<(input: CharStream, config: LexerATNConfig, trans: Transition, configs: ATNConfigSet, @@ -109,8 +105,7 @@ export class LexerATNSimulator extends ATNSimulator { this.recognizer = recog; if (recog) { - this.#minCodePoint = recog.options.minCodePoint; - this.#maxCodePoint = recog.options.maxCodePoint; + this.#options = recog.options; } } @@ -261,7 +256,7 @@ export class LexerATNSimulator extends ATNSimulator { * `t`, or `null` if the target state for this edge is not already cached */ private getExistingTargetState(s: DFAState, t: number): DFAState | undefined { - if (t > Token.EOF && t < LexerATNSimulator.MAX_DFA_EDGE) { + if (t >= this.#options.minDFAEdge && t <= this.#options.maxDFAEdge) { return s.edges[t]; } @@ -364,7 +359,7 @@ export class LexerATNSimulator extends ATNSimulator { } private getReachableTarget(trans: Transition, t: number): ATNState | undefined { - if (trans.matches(t, this.#minCodePoint, this.#maxCodePoint)) { + if (trans.matches(t, this.#options.minCodePoint, this.#options.maxCodePoint)) { return trans.target; } else { return undefined; @@ -537,7 +532,7 @@ export class LexerATNSimulator extends ATNSimulator { trans: Transition, configs: ATNConfigSet, speculative: boolean, treatEofAsEpsilon: boolean) => { if (treatEofAsEpsilon) { - if (trans.matches(Token.EOF, this.#minCodePoint, this.#maxCodePoint)) { + if (trans.matches(Token.EOF, this.#options.minCodePoint, this.#options.maxCodePoint)) { return LexerATNConfig.createWithConfig(trans.target, config); } } @@ -630,7 +625,7 @@ export class LexerATNSimulator extends ATNSimulator { } // Add the edge. - if (tk === Token.EOF || tk > LexerATNSimulator.MAX_DFA_EDGE) { + if (tk < this.#options.minDFAEdge || tk > this.#options.maxDFAEdge) { // Only track edges within the DFA bounds return to!; } diff --git a/src/atn/ParserATNSimulator.ts b/src/atn/ParserATNSimulator.ts index 55e3089..1f6b105 100644 --- a/src/atn/ParserATNSimulator.ts +++ b/src/atn/ParserATNSimulator.ts @@ -1281,7 +1281,7 @@ export class ParserATNSimulator extends ATNSimulator { } } - c.reachesIntoOuterContext = 1; + c.reachesIntoOuterContext = true; if (closureBusy.getOrAdd(c) !== c) { // Avoid infinite recursion for right-recursive rules. continue;