UNPKG

bitmark-grammar

Version:
565 lines (498 loc) 22.1 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.2796692-07:00 import { Array2DHashMap } from "../misc/Array2DHashMap"; import { ATNState } from "./ATNState"; import { DecisionState } from "./DecisionState"; import { Equatable } from "../misc/Stubs"; import { LexerActionExecutor } from "./LexerActionExecutor"; import { MurmurHash } from "../misc/MurmurHash"; import { NotNull, Override } from "../Decorators"; import { ObjectEqualityComparator } from "../misc/ObjectEqualityComparator"; import { PredictionContext } from "./PredictionContext"; import { PredictionContextCache } from "./PredictionContextCache"; import { Recognizer } from "../Recognizer"; import { SemanticContext } from "./SemanticContext"; import * as assert from "assert"; /** * This field stores the bit mask for implementing the * {@link #isPrecedenceFilterSuppressed} property as a bit within the * existing {@link #altAndOuterContextDepth} field. */ const SUPPRESS_PRECEDENCE_FILTER: number = 0x80000000; /** * Represents a location with context in an ATN. The location is identified by the following values: * * * The current ATN state * * The predicted alternative * * The semantic context which must be true for this configuration to be enabled * * The syntactic context, which is represented as a graph-structured stack whose path(s) lead to the root of the rule * invocations leading to this state * * In addition to these values, `ATNConfig` stores several properties about paths taken to get to the location which * were added over time to help with performance, correctness, and/or debugging. * * * `reachesIntoOuterContext`:: Used to ensure semantic predicates are not evaluated in the wrong context. * * `hasPassedThroughNonGreedyDecision`: Used for enabling first-match-wins instead of longest-match-wins after * crossing a non-greedy decision. * * `lexerActionExecutor`: Used for tracking the lexer action(s) to execute should this instance be selected during * lexing. * * `isPrecedenceFilterSuppressed`: A state variable for one of the dynamic disambiguation strategies employed by * `ParserATNSimulator.applyPrecedenceFilter`. * * Due to the use of a graph-structured stack, a single `ATNConfig` is capable of representing many individual ATN * configurations which reached the same location in an ATN by following different paths. * * PERF: To conserve memory, `ATNConfig` is split into several different concrete types. `ATNConfig` itself stores the * minimum amount of information typically used to define an `ATNConfig` instance. Various derived types provide * additional storage space for cases where a non-default value is used for some of the object properties. The * `ATNConfig.create` and `ATNConfig.transform` methods automatically select the smallest concrete type capable of * representing the unique information for any given `ATNConfig`. */ export class ATNConfig implements Equatable { /** The ATN state associated with this configuration */ @NotNull private _state: ATNState; /** * This is a bit-field currently containing the following values. * * * 0x00FFFFFF: Alternative * * 0x7F000000: Outer context depth * * 0x80000000: Suppress precedence filter */ private altAndOuterContextDepth: number; /** The stack of invoking states leading to the rule/states associated * with this config. We track only those contexts pushed during * execution of the ATN simulator. */ @NotNull private _context: PredictionContext; constructor(/*@NotNull*/ state: ATNState, alt: number, /*@NotNull*/ context: PredictionContext); constructor(/*@NotNull*/ state: ATNState, /*@NotNull*/ c: ATNConfig, /*@NotNull*/ context: PredictionContext); constructor(@NotNull state: ATNState, altOrConfig: number | ATNConfig, @NotNull context: PredictionContext) { if (typeof altOrConfig === "number") { assert((altOrConfig & 0xFFFFFF) === altOrConfig); this._state = state; this.altAndOuterContextDepth = altOrConfig; this._context = context; } else { this._state = state; this.altAndOuterContextDepth = altOrConfig.altAndOuterContextDepth; this._context = context; } } public static create(/*@NotNull*/ state: ATNState, alt: number, context: PredictionContext): ATNConfig; public static create(/*@NotNull*/ state: ATNState, alt: number, context: PredictionContext, /*@NotNull*/ semanticContext: SemanticContext): ATNConfig; public static create(/*@NotNull*/ state: ATNState, alt: number, context: PredictionContext, /*@*/ semanticContext: SemanticContext, lexerActionExecutor: LexerActionExecutor | undefined): ATNConfig; public static create(@NotNull state: ATNState, alt: number, context: PredictionContext, @NotNull semanticContext: SemanticContext = SemanticContext.NONE, lexerActionExecutor?: LexerActionExecutor): ATNConfig { if (semanticContext !== SemanticContext.NONE) { if (lexerActionExecutor != null) { return new ActionSemanticContextATNConfig(lexerActionExecutor, semanticContext, state, alt, context, false); } else { return new SemanticContextATNConfig(semanticContext, state, alt, context); } } else if (lexerActionExecutor != null) { return new ActionATNConfig(lexerActionExecutor, state, alt, context, false); } else { return new ATNConfig(state, alt, context); } } /** Gets the ATN state associated with this configuration */ @NotNull get state(): ATNState { return this._state; } /** What alt (or lexer rule) is predicted by this configuration */ get alt(): number { return this.altAndOuterContextDepth & 0x00FFFFFF; } @NotNull get context(): PredictionContext { return this._context; } set context(@NotNull context: PredictionContext) { this._context = context; } get reachesIntoOuterContext(): boolean { return this.outerContextDepth !== 0; } /** * We cannot execute predicates dependent upon local context unless * we know for sure we are in the correct context. Because there is * no way to do this efficiently, we simply cannot evaluate * dependent predicates unless we are in the rule that initially * invokes the ATN simulator. * * closure() tracks the depth of how far we dip into the outer context: * depth &gt; 0. Note that it may not be totally accurate depth since I * don't ever decrement. TODO: make it a boolean then */ get outerContextDepth(): number { return (this.altAndOuterContextDepth >>> 24) & 0x7F; } set outerContextDepth(outerContextDepth: number) { assert(outerContextDepth >= 0); // saturate at 0x7F - everything but zero/positive is only used for debug information anyway outerContextDepth = Math.min(outerContextDepth, 0x7F); this.altAndOuterContextDepth = ((outerContextDepth << 24) | (this.altAndOuterContextDepth & ~0x7F000000) >>> 0); } get lexerActionExecutor(): LexerActionExecutor | undefined { return undefined; } @NotNull get semanticContext(): SemanticContext { return SemanticContext.NONE; } get hasPassedThroughNonGreedyDecision(): boolean { return false; } @Override public clone(): ATNConfig { return this.transform(this.state, false); } public transform(/*@NotNull*/ state: ATNState, checkNonGreedy: boolean): ATNConfig; public transform(/*@NotNull*/ state: ATNState, checkNonGreedy: boolean, /*@NotNull*/ semanticContext: SemanticContext): ATNConfig; public transform(/*@NotNull*/ state: ATNState, checkNonGreedy: boolean, context: PredictionContext): ATNConfig; public transform(/*@NotNull*/ state: ATNState, checkNonGreedy: boolean, lexerActionExecutor: LexerActionExecutor): ATNConfig; public transform(/*@NotNull*/ state: ATNState, checkNonGreedy: boolean, arg2?: SemanticContext | PredictionContext | LexerActionExecutor): ATNConfig { if (arg2 == null) { return this.transformImpl(state, this._context, this.semanticContext, checkNonGreedy, this.lexerActionExecutor); } else if (arg2 instanceof PredictionContext) { return this.transformImpl(state, arg2, this.semanticContext, checkNonGreedy, this.lexerActionExecutor); } else if (arg2 instanceof SemanticContext) { return this.transformImpl(state, this._context, arg2, checkNonGreedy, this.lexerActionExecutor); } else { return this.transformImpl(state, this._context, this.semanticContext, checkNonGreedy, arg2); } } private transformImpl(@NotNull state: ATNState, context: PredictionContext, @NotNull semanticContext: SemanticContext, checkNonGreedy: boolean, lexerActionExecutor: LexerActionExecutor | undefined): ATNConfig { let passedThroughNonGreedy: boolean = checkNonGreedy && ATNConfig.checkNonGreedyDecision(this, state); if (semanticContext !== SemanticContext.NONE) { if (lexerActionExecutor != null || passedThroughNonGreedy) { return new ActionSemanticContextATNConfig(lexerActionExecutor, semanticContext, state, this, context, passedThroughNonGreedy); } else { return new SemanticContextATNConfig(semanticContext, state, this, context); } } else if (lexerActionExecutor != null || passedThroughNonGreedy) { return new ActionATNConfig(lexerActionExecutor, state, this, context, passedThroughNonGreedy); } else { return new ATNConfig(state, this, context); } } private static checkNonGreedyDecision(source: ATNConfig, target: ATNState): boolean { return source.hasPassedThroughNonGreedyDecision || target instanceof DecisionState && target.nonGreedy; } public appendContext(context: number, contextCache: PredictionContextCache): ATNConfig; public appendContext(context: PredictionContext, contextCache: PredictionContextCache): ATNConfig; public appendContext(context: number | PredictionContext, contextCache: PredictionContextCache): ATNConfig { if (typeof context === "number") { let appendedContext: PredictionContext = this.context.appendSingleContext(context, contextCache); let result: ATNConfig = this.transform(this.state, false, appendedContext); return result; } else { let appendedContext: PredictionContext = this.context.appendContext(context, contextCache); let result: ATNConfig = this.transform(this.state, false, appendedContext); return result; } } /** * Determines if this `ATNConfig` fully contains another `ATNConfig`. * * An ATN configuration represents a position (including context) in an ATN during parsing. Since `ATNConfig` stores * the context as a graph, a single `ATNConfig` instance is capable of representing many ATN configurations which * are all in the same "location" but have different contexts. These `ATNConfig` instances are again merged when * they are added to an `ATNConfigSet`. This method supports `ATNConfigSet.contains` by evaluating whether a * particular `ATNConfig` contains all of the ATN configurations represented by another `ATNConfig`. * * An `ATNConfig` _a_ contains another `ATNConfig` _b_ if all of the following conditions are met: * * * The configurations are in the same state (`state`) * * The configurations predict the same alternative (`alt`) * * The semantic context of _a_ implies the semantic context of _b_ (this method performs a weaker equality check) * * Joining the prediction contexts of _a_ and _b_ results in the prediction context of _a_ * * This method implements a conservative approximation of containment. As a result, when this method returns `true` * it is known that parsing from `subconfig` can only recognize a subset of the inputs which can be recognized * starting at the current `ATNConfig`. However, due to the imprecise evaluation of implication for the semantic * contexts, no assumptions can be made about the relationship between the configurations when this method returns * `false`. * * @param subconfig The sub configuration. * @returns `true` if this configuration contains `subconfig`; otherwise, `false`. */ public contains(subconfig: ATNConfig): boolean { if (this.state.stateNumber !== subconfig.state.stateNumber || this.alt !== subconfig.alt || !this.semanticContext.equals(subconfig.semanticContext)) { return false; } let leftWorkList: PredictionContext[] = []; let rightWorkList: PredictionContext[] = []; leftWorkList.push(this.context); rightWorkList.push(subconfig.context); while (true) { let left = leftWorkList.pop(); let right = rightWorkList.pop(); if (!left || !right) { break; } if (left === right) { return true; } if (left.size < right.size) { return false; } if (right.isEmpty) { return left.hasEmpty; } else { for (let i = 0; i < right.size; i++) { let index: number = left.findReturnState(right.getReturnState(i)); if (index < 0) { // assumes invokingStates has no duplicate entries return false; } leftWorkList.push(left.getParent(index)); rightWorkList.push(right.getParent(i)); } } } return false; } get isPrecedenceFilterSuppressed(): boolean { return (this.altAndOuterContextDepth & SUPPRESS_PRECEDENCE_FILTER) !== 0; } set isPrecedenceFilterSuppressed(value: boolean) { if (value) { this.altAndOuterContextDepth |= SUPPRESS_PRECEDENCE_FILTER; } else { this.altAndOuterContextDepth &= ~SUPPRESS_PRECEDENCE_FILTER; } } /** An ATN configuration is equal to another if both have * the same state, they predict the same alternative, and * syntactic/semantic contexts are the same. */ @Override public equals(o: any): boolean { if (this === o) { return true; } else if (!(o instanceof ATNConfig)) { return false; } return this.state.stateNumber === o.state.stateNumber && this.alt === o.alt && this.reachesIntoOuterContext === o.reachesIntoOuterContext && this.context.equals(o.context) && this.semanticContext.equals(o.semanticContext) && this.isPrecedenceFilterSuppressed === o.isPrecedenceFilterSuppressed && this.hasPassedThroughNonGreedyDecision === o.hasPassedThroughNonGreedyDecision && ObjectEqualityComparator.INSTANCE.equals(this.lexerActionExecutor, o.lexerActionExecutor); } @Override public hashCode(): number { let hashCode: number = MurmurHash.initialize(7); hashCode = MurmurHash.update(hashCode, this.state.stateNumber); hashCode = MurmurHash.update(hashCode, this.alt); hashCode = MurmurHash.update(hashCode, this.reachesIntoOuterContext ? 1 : 0); hashCode = MurmurHash.update(hashCode, this.context); hashCode = MurmurHash.update(hashCode, this.semanticContext); hashCode = MurmurHash.update(hashCode, this.hasPassedThroughNonGreedyDecision ? 1 : 0); hashCode = MurmurHash.update(hashCode, this.lexerActionExecutor); hashCode = MurmurHash.finish(hashCode, 7); return hashCode; } /** * Returns a graphical representation of the current `ATNConfig` in Graphviz format. The graph can be stored to a * **.dot** file and then rendered to an image using Graphviz. * * @returns A Graphviz graph representing the current `ATNConfig`. * * @see http://www.graphviz.org/ */ public toDotString(): string { let builder = ""; builder += ("digraph G {\n"); builder += ("rankdir=LR;\n"); let visited = new Array2DHashMap<PredictionContext, number>(PredictionContext.IdentityEqualityComparator.INSTANCE); let workList: PredictionContext[] = []; function getOrAddContext(context: PredictionContext): number { let newNumber = visited.size; let result = visited.putIfAbsent(context, newNumber); if (result != null) { // Already saw this context return result; } workList.push(context); return newNumber; } workList.push(this.context); visited.put(this.context, 0); while (true) { let current = workList.pop(); if (!current) { break; } for (let i = 0; i < current.size; i++) { builder += (" s") + (getOrAddContext(current)); builder += ("->"); builder += ("s") + (getOrAddContext(current.getParent(i))); builder += ("[label=\"") + (current.getReturnState(i)) + ("\"];\n"); } } builder += ("}\n"); return builder.toString(); } public toString(): string; public toString(recog: Recognizer<any, any> | undefined, showAlt: boolean): string; public toString(recog: Recognizer<any, any> | undefined, showAlt: boolean, showContext: boolean): string; public toString(recog?: Recognizer<any, any>, showAlt?: boolean, showContext?: boolean): string { // Must check showContext before showAlt to preserve original overload behavior if (showContext == null) { showContext = showAlt != null; } if (showAlt == null) { showAlt = true; } let buf = ""; // if (this.state.ruleIndex >= 0) { // if (recog != null) { // buf += (recog.ruleNames[this.state.ruleIndex] + ":"); // } else { // buf += (this.state.ruleIndex + ":"); // } // } let contexts: string[]; if (showContext) { contexts = this.context.toStrings(recog, this.state.stateNumber); } else { contexts = ["?"]; } let first: boolean = true; for (let contextDesc of contexts) { if (first) { first = false; } else { buf += (", "); } buf += ("("); buf += (this.state); if (showAlt) { buf += (","); buf += (this.alt); } if (this.context) { buf += (","); buf += (contextDesc); } if (this.semanticContext !== SemanticContext.NONE) { buf += (","); buf += (this.semanticContext); } if (this.reachesIntoOuterContext) { buf += (",up=") + (this.outerContextDepth); } buf += (")"); } return buf.toString(); } } /** * This class was derived from `ATNConfig` purely as a memory optimization. It allows for the creation of an `ATNConfig` * with a non-default semantic context. * * See the `ATNConfig` documentation for more information about conserving memory through the use of several concrete * types. */ class SemanticContextATNConfig extends ATNConfig { @NotNull private _semanticContext: SemanticContext; constructor(semanticContext: SemanticContext, /*@NotNull*/ state: ATNState, alt: number, context: PredictionContext); constructor(semanticContext: SemanticContext, /*@NotNull*/ state: ATNState, /*@NotNull*/ c: ATNConfig, context: PredictionContext); constructor(semanticContext: SemanticContext, @NotNull state: ATNState, @NotNull altOrConfig: number | ATNConfig, context: PredictionContext) { if (typeof altOrConfig === "number") { super(state, altOrConfig, context); } else { super(state, altOrConfig, context); } this._semanticContext = semanticContext; } @Override get semanticContext(): SemanticContext { return this._semanticContext; } } /** * This class was derived from `ATNConfig` purely as a memory optimization. It allows for the creation of an `ATNConfig` * with a lexer action. * * See the `ATNConfig` documentation for more information about conserving memory through the use of several concrete * types. */ class ActionATNConfig extends ATNConfig { private _lexerActionExecutor?: LexerActionExecutor; private passedThroughNonGreedyDecision: boolean; constructor(lexerActionExecutor: LexerActionExecutor | undefined, /*@NotNull*/ state: ATNState, alt: number, context: PredictionContext, passedThroughNonGreedyDecision: boolean); constructor(lexerActionExecutor: LexerActionExecutor | undefined, /*@NotNull*/ state: ATNState, /*@NotNull*/ c: ATNConfig, context: PredictionContext, passedThroughNonGreedyDecision: boolean); constructor(lexerActionExecutor: LexerActionExecutor | undefined, @NotNull state: ATNState, @NotNull altOrConfig: number | ATNConfig, context: PredictionContext, passedThroughNonGreedyDecision: boolean) { if (typeof altOrConfig === "number") { super(state, altOrConfig, context); } else { super(state, altOrConfig, context); if (altOrConfig.semanticContext !== SemanticContext.NONE) { throw new Error("Not supported"); } } this._lexerActionExecutor = lexerActionExecutor; this.passedThroughNonGreedyDecision = passedThroughNonGreedyDecision; } @Override get lexerActionExecutor(): LexerActionExecutor | undefined { return this._lexerActionExecutor; } @Override get hasPassedThroughNonGreedyDecision(): boolean { return this.passedThroughNonGreedyDecision; } } /** * This class was derived from `SemanticContextATNConfig` purely as a memory optimization. It allows for the creation of * an `ATNConfig` with both a lexer action and a non-default semantic context. * * See the `ATNConfig` documentation for more information about conserving memory through the use of several concrete * types. */ class ActionSemanticContextATNConfig extends SemanticContextATNConfig { private _lexerActionExecutor?: LexerActionExecutor; private passedThroughNonGreedyDecision: boolean; constructor(lexerActionExecutor: LexerActionExecutor | undefined, /*@NotNull*/ semanticContext: SemanticContext, /*@NotNull*/ state: ATNState, alt: number, context: PredictionContext, passedThroughNonGreedyDecision: boolean); constructor(lexerActionExecutor: LexerActionExecutor | undefined, /*@NotNull*/ semanticContext: SemanticContext, /*@NotNull*/ state: ATNState, /*@NotNull*/ c: ATNConfig, context: PredictionContext, passedThroughNonGreedyDecision: boolean); constructor(lexerActionExecutor: LexerActionExecutor | undefined, @NotNull semanticContext: SemanticContext, @NotNull state: ATNState, altOrConfig: number | ATNConfig, context: PredictionContext, passedThroughNonGreedyDecision: boolean) { if (typeof altOrConfig === "number") { super(semanticContext, state, altOrConfig, context); } else { super(semanticContext, state, altOrConfig, context); } this._lexerActionExecutor = lexerActionExecutor; this.passedThroughNonGreedyDecision = passedThroughNonGreedyDecision; } @Override get lexerActionExecutor(): LexerActionExecutor | undefined { return this._lexerActionExecutor; } @Override get hasPassedThroughNonGreedyDecision(): boolean { return this.passedThroughNonGreedyDecision; } }