UNPKG

bitmark-grammar

Version:
271 lines (231 loc) 9.03 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.1063510-07:00 import { Array2DHashMap } from "../misc/Array2DHashMap"; import { ATNState } from "./ATNState"; import { ATNType } from "./ATNType"; import { DecisionState } from "./DecisionState"; import { DFA } from "../dfa/DFA"; import { IntervalSet } from "../misc/IntervalSet"; import { InvalidState } from "./InvalidState"; import { LexerAction } from "./LexerAction"; import { LL1Analyzer } from "./LL1Analyzer"; import { NotNull } from "../Decorators"; import { ObjectEqualityComparator } from "../misc/ObjectEqualityComparator"; import { PredictionContext } from "./PredictionContext"; import { RuleContext } from "../RuleContext"; import { RuleStartState } from "./RuleStartState"; import { RuleStopState } from "./RuleStopState"; import { RuleTransition } from "./RuleTransition"; import { Token } from "../Token"; import { TokensStartState } from "./TokensStartState"; import * as assert from "assert"; /** */ export class ATN { @NotNull public readonly states: ATNState[] = []; /** Each subrule/rule is a decision point and we must track them so we * can go back later and build DFA predictors for them. This includes * all the rules, subrules, optional blocks, ()+, ()* etc... */ @NotNull public decisionToState: DecisionState[] = []; /** * Maps from rule index to starting state number. */ public ruleToStartState: RuleStartState[]; /** * Maps from rule index to stop state number. */ public ruleToStopState: RuleStopState[]; @NotNull public modeNameToStartState: Map<string, TokensStartState> = new Map<string, TokensStartState>(); /** * The type of the ATN. */ public grammarType: ATNType; /** * The maximum value for any symbol recognized by a transition in the ATN. */ public maxTokenType: number; /** * For lexer ATNs, this maps the rule index to the resulting token type. * For parser ATNs, this maps the rule index to the generated bypass token * type if the * {@link ATNDeserializationOptions#isGenerateRuleBypassTransitions} * deserialization option was specified; otherwise, this is `undefined`. */ public ruleToTokenType: Int32Array; /** * For lexer ATNs, this is an array of {@link LexerAction} objects which may * be referenced by action transitions in the ATN. */ public lexerActions: LexerAction[]; @NotNull public modeToStartState: TokensStartState[] = []; private contextCache: Array2DHashMap<PredictionContext, PredictionContext> = new Array2DHashMap<PredictionContext, PredictionContext>(ObjectEqualityComparator.INSTANCE); @NotNull public decisionToDFA: DFA[] = []; @NotNull public modeToDFA: DFA[] = []; public LL1Table: Map<number, number> = new Map<number, number>(); /** Used for runtime deserialization of ATNs from strings */ constructor(@NotNull grammarType: ATNType, maxTokenType: number) { this.grammarType = grammarType; this.maxTokenType = maxTokenType; } public clearDFA(): void { this.decisionToDFA = new Array<DFA>(this.decisionToState.length); for (let i = 0; i < this.decisionToDFA.length; i++) { this.decisionToDFA[i] = new DFA(this.decisionToState[i], i); } this.modeToDFA = new Array<DFA>(this.modeToStartState.length); for (let i = 0; i < this.modeToDFA.length; i++) { this.modeToDFA[i] = new DFA(this.modeToStartState[i]); } this.contextCache.clear(); this.LL1Table.clear(); } get contextCacheSize(): number { return this.contextCache.size; } public getCachedContext(context: PredictionContext): PredictionContext { return PredictionContext.getCachedContext(context, this.contextCache, new PredictionContext.IdentityHashMap()); } public getDecisionToDFA(): DFA[] { assert(this.decisionToDFA != null && this.decisionToDFA.length === this.decisionToState.length); return this.decisionToDFA; } /** Compute the set of valid tokens that can occur starting in state `s`. * If `ctx` is {@link PredictionContext#EMPTY_LOCAL}, the set of tokens will not include what can follow * the rule surrounding `s`. In other words, the set will be * restricted to tokens reachable staying within `s`'s rule. */ // @NotNull public nextTokens(s: ATNState, /*@NotNull*/ ctx: PredictionContext): IntervalSet; /** * Compute the set of valid tokens that can occur starting in `s` and * staying in same rule. {@link Token#EPSILON} is in set if we reach end of * rule. */ // @NotNull public nextTokens(/*@NotNull*/ s: ATNState): IntervalSet; @NotNull public nextTokens(s: ATNState, ctx?: PredictionContext): IntervalSet { if (ctx) { let anal: LL1Analyzer = new LL1Analyzer(this); let next: IntervalSet = anal.LOOK(s, ctx); return next; } else { if (s.nextTokenWithinRule) { return s.nextTokenWithinRule; } s.nextTokenWithinRule = this.nextTokens(s, PredictionContext.EMPTY_LOCAL); s.nextTokenWithinRule.setReadonly(true); return s.nextTokenWithinRule; } } public addState(state: ATNState): void { state.atn = this; state.stateNumber = this.states.length; this.states.push(state); } public removeState(@NotNull state: ATNState): void { // just replace the state, don't shift states in list let invalidState = new InvalidState(); invalidState.atn = this; invalidState.stateNumber = state.stateNumber; this.states[state.stateNumber] = invalidState; } public defineMode(@NotNull name: string, @NotNull s: TokensStartState): void { this.modeNameToStartState.set(name, s); this.modeToStartState.push(s); this.modeToDFA.push(new DFA(s)); this.defineDecisionState(s); } public defineDecisionState(@NotNull s: DecisionState): number { this.decisionToState.push(s); s.decision = this.decisionToState.length - 1; this.decisionToDFA.push(new DFA(s, s.decision)); return s.decision; } public getDecisionState(decision: number): DecisionState | undefined { if (this.decisionToState.length > 0) { return this.decisionToState[decision]; } return undefined; } get numberOfDecisions(): number { return this.decisionToState.length; } /** * Computes the set of input symbols which could follow ATN state number * `stateNumber` in the specified full `context`. This method * considers the complete parser context, but does not evaluate semantic * predicates (i.e. all predicates encountered during the calculation are * assumed true). If a path in the ATN exists from the starting state to the * {@link RuleStopState} of the outermost context without matching any * symbols, {@link Token#EOF} is added to the returned set. * * If `context` is `undefined`, it is treated as * {@link ParserRuleContext#EMPTY}. * * Note that this does NOT give you the set of all tokens that could * appear at a given token position in the input phrase. In other words, it * does not answer: * * > Given a specific partial input phrase, return the set of all * > tokens that can follow the last token in the input phrase. * * The big difference is that with just the input, the parser could land * right in the middle of a lookahead decision. Getting all * *possible* tokens given a partial input stream is a separate * computation. See https://github.com/antlr/antlr4/issues/1428 * * For this function, we are specifying an ATN state and call stack to * compute what token(s) can come next and specifically: outside of a * lookahead decision. That is what you want for error reporting and * recovery upon parse error. * * @param stateNumber the ATN state number * @param context the full parse context * @returns The set of potentially valid input symbols which could follow the * specified state in the specified context. * @ if the ATN does not contain a state with * number `stateNumber` */ @NotNull public getExpectedTokens(stateNumber: number, context: RuleContext | undefined): IntervalSet { if (stateNumber < 0 || stateNumber >= this.states.length) { throw new RangeError("Invalid state number."); } let ctx: RuleContext | undefined = context; let s: ATNState = this.states[stateNumber]; let following: IntervalSet = this.nextTokens(s); if (!following.contains(Token.EPSILON)) { return following; } let expected: IntervalSet = new IntervalSet(); expected.addAll(following); expected.remove(Token.EPSILON); while (ctx != null && ctx.invokingState >= 0 && following.contains(Token.EPSILON)) { let invokingState: ATNState = this.states[ctx.invokingState]; let rt: RuleTransition = invokingState.transition(0) as RuleTransition; following = this.nextTokens(rt.followState); expected.addAll(following); expected.remove(Token.EPSILON); ctx = ctx._parent; } if (following.contains(Token.EPSILON)) { expected.add(Token.EOF); } return expected; } } export namespace ATN { export const INVALID_ALT_NUMBER: number = 0; }