bitmark-grammar
Version:
271 lines (231 loc) • 9.03 kB
text/typescript
/*!
* 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 {
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...
*/
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[];
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[];
public modeToStartState: TokensStartState[] = [];
private contextCache: Array2DHashMap<PredictionContext, PredictionContext> =
new Array2DHashMap<PredictionContext, PredictionContext>(ObjectEqualityComparator.INSTANCE);
public decisionToDFA: DFA[] = [];
public modeToDFA: DFA[] = [];
public LL1Table: Map<number, number> = new Map<number, number>();
/** Used for runtime deserialization of ATNs from strings */
constructor( 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;
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( 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( name: string, s: TokensStartState): void {
this.modeNameToStartState.set(name, s);
this.modeToStartState.push(s);
this.modeToDFA.push(new DFA(s));
this.defineDecisionState(s);
}
public defineDecisionState( 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`
*/
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;
}