bitmark-grammar
Version:
757 lines (617 loc) • 22.1 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:35.3812636-07:00
import { Array2DHashMap } from "../misc/Array2DHashMap";
import { Array2DHashSet } from "../misc/Array2DHashSet";
import { Arrays } from "../misc/Arrays";
import { ATN } from "./ATN";
import { ATNState } from "./ATNState";
import { EqualityComparator } from "../misc/EqualityComparator";
import { MurmurHash } from "../misc/MurmurHash";
import { NotNull, Override } from "../Decorators";
import { Equatable, JavaSet } from "../misc/Stubs";
import { PredictionContextCache } from "./PredictionContextCache";
import { Recognizer } from "../Recognizer";
import { RuleContext } from "../RuleContext";
import { RuleTransition } from "./RuleTransition";
import * as assert from "assert";
const INITIAL_HASH: number = 1;
export abstract class PredictionContext implements Equatable {
/**
* Stores the computed hash code of this {@link PredictionContext}. The hash
* code is computed in parts to match the following reference algorithm.
*
* ```
* private int referenceHashCode() {
* int hash = {@link MurmurHash#initialize MurmurHash.initialize}({@link #INITIAL_HASH});
*
* for (int i = 0; i < this.size; i++) {
* hash = {@link MurmurHash#update MurmurHash.update}(hash, {@link #getParent getParent}(i));
* }
*
* for (int i = 0; i < this.size; i++) {
* hash = {@link MurmurHash#update MurmurHash.update}(hash, {@link #getReturnState getReturnState}(i));
* }
*
* hash = {@link MurmurHash#finish MurmurHash.finish}(hash, 2 * this.size);
* return hash;
* }
* ```
*/
private readonly cachedHashCode: number;
constructor(cachedHashCode: number) {
this.cachedHashCode = cachedHashCode;
}
protected static calculateEmptyHashCode(): number {
let hash: number = MurmurHash.initialize(INITIAL_HASH);
hash = MurmurHash.finish(hash, 0);
return hash;
}
protected static calculateSingleHashCode(parent: PredictionContext, returnState: number): number {
let hash: number = MurmurHash.initialize(INITIAL_HASH);
hash = MurmurHash.update(hash, parent);
hash = MurmurHash.update(hash, returnState);
hash = MurmurHash.finish(hash, 2);
return hash;
}
protected static calculateHashCode(parents: PredictionContext[], returnStates: number[]): number {
let hash: number = MurmurHash.initialize(INITIAL_HASH);
for (let parent of parents) {
hash = MurmurHash.update(hash, parent);
}
for (let returnState of returnStates) {
hash = MurmurHash.update(hash, returnState);
}
hash = MurmurHash.finish(hash, 2 * parents.length);
return hash;
}
public abstract readonly size: number;
public abstract getReturnState(index: number): number;
public abstract findReturnState(returnState: number): number;
// @NotNull
public abstract getParent(index: number): PredictionContext;
protected abstract addEmptyContext(): PredictionContext;
protected abstract removeEmptyContext(): PredictionContext;
public static fromRuleContext(atn: ATN, outerContext: RuleContext, fullContext: boolean = true): PredictionContext {
if (outerContext.isEmpty) {
return fullContext ? PredictionContext.EMPTY_FULL : PredictionContext.EMPTY_LOCAL;
}
let parent: PredictionContext;
if (outerContext._parent) {
parent = PredictionContext.fromRuleContext(atn, outerContext._parent, fullContext);
} else {
parent = fullContext ? PredictionContext.EMPTY_FULL : PredictionContext.EMPTY_LOCAL;
}
let state: ATNState = atn.states[outerContext.invokingState];
let transition: RuleTransition = state.transition(0) as RuleTransition;
return parent.getChild(transition.followState.stateNumber);
}
private static addEmptyContext(context: PredictionContext): PredictionContext {
return context.addEmptyContext();
}
private static removeEmptyContext(context: PredictionContext): PredictionContext {
return context.removeEmptyContext();
}
public static join( context0: PredictionContext, context1: PredictionContext, contextCache: PredictionContextCache = PredictionContextCache.UNCACHED): PredictionContext {
if (context0 === context1) {
return context0;
}
if (context0.isEmpty) {
return PredictionContext.isEmptyLocal(context0) ? context0 : PredictionContext.addEmptyContext(context1);
} else if (context1.isEmpty) {
return PredictionContext.isEmptyLocal(context1) ? context1 : PredictionContext.addEmptyContext(context0);
}
let context0size: number = context0.size;
let context1size: number = context1.size;
if (context0size === 1 && context1size === 1 && context0.getReturnState(0) === context1.getReturnState(0)) {
let merged: PredictionContext = contextCache.join(context0.getParent(0), context1.getParent(0));
if (merged === context0.getParent(0)) {
return context0;
} else if (merged === context1.getParent(0)) {
return context1;
} else {
return merged.getChild(context0.getReturnState(0));
}
}
let count: number = 0;
let parentsList: PredictionContext[] = new Array<PredictionContext>(context0size + context1size);
let returnStatesList: number[] = new Array<number>(parentsList.length);
let leftIndex: number = 0;
let rightIndex: number = 0;
let canReturnLeft: boolean = true;
let canReturnRight: boolean = true;
while (leftIndex < context0size && rightIndex < context1size) {
if (context0.getReturnState(leftIndex) === context1.getReturnState(rightIndex)) {
parentsList[count] = contextCache.join(context0.getParent(leftIndex), context1.getParent(rightIndex));
returnStatesList[count] = context0.getReturnState(leftIndex);
canReturnLeft = canReturnLeft && parentsList[count] === context0.getParent(leftIndex);
canReturnRight = canReturnRight && parentsList[count] === context1.getParent(rightIndex);
leftIndex++;
rightIndex++;
} else if (context0.getReturnState(leftIndex) < context1.getReturnState(rightIndex)) {
parentsList[count] = context0.getParent(leftIndex);
returnStatesList[count] = context0.getReturnState(leftIndex);
canReturnRight = false;
leftIndex++;
} else {
assert(context1.getReturnState(rightIndex) < context0.getReturnState(leftIndex));
parentsList[count] = context1.getParent(rightIndex);
returnStatesList[count] = context1.getReturnState(rightIndex);
canReturnLeft = false;
rightIndex++;
}
count++;
}
while (leftIndex < context0size) {
parentsList[count] = context0.getParent(leftIndex);
returnStatesList[count] = context0.getReturnState(leftIndex);
leftIndex++;
canReturnRight = false;
count++;
}
while (rightIndex < context1size) {
parentsList[count] = context1.getParent(rightIndex);
returnStatesList[count] = context1.getReturnState(rightIndex);
rightIndex++;
canReturnLeft = false;
count++;
}
if (canReturnLeft) {
return context0;
} else if (canReturnRight) {
return context1;
}
if (count < parentsList.length) {
parentsList = parentsList.slice(0, count);
returnStatesList = returnStatesList.slice(0, count);
}
if (parentsList.length === 0) {
// if one of them was EMPTY_LOCAL, it would be empty and handled at the beginning of the method
return PredictionContext.EMPTY_FULL;
} else if (parentsList.length === 1) {
return new SingletonPredictionContext(parentsList[0], returnStatesList[0]);
} else {
return new ArrayPredictionContext(parentsList, returnStatesList);
}
}
public static isEmptyLocal(context: PredictionContext): boolean {
return context === PredictionContext.EMPTY_LOCAL;
}
public static getCachedContext(
context: PredictionContext,
contextCache: Array2DHashMap<PredictionContext, PredictionContext>,
visited: PredictionContext.IdentityHashMap): PredictionContext {
if (context.isEmpty) {
return context;
}
let existing = visited.get(context);
if (existing) {
return existing;
}
existing = contextCache.get(context);
if (existing) {
visited.put(context, existing);
return existing;
}
let changed: boolean = false;
let parents: PredictionContext[] = new Array<PredictionContext>(context.size);
for (let i = 0; i < parents.length; i++) {
let parent: PredictionContext = PredictionContext.getCachedContext(context.getParent(i), contextCache, visited);
if (changed || parent !== context.getParent(i)) {
if (!changed) {
parents = new Array<PredictionContext>(context.size);
for (let j = 0; j < context.size; j++) {
parents[j] = context.getParent(j);
}
changed = true;
}
parents[i] = parent;
}
}
if (!changed) {
existing = contextCache.putIfAbsent(context, context);
visited.put(context, existing != null ? existing : context);
return context;
}
// We know parents.length>0 because context.isEmpty is checked at the beginning of the method.
let updated: PredictionContext;
if (parents.length === 1) {
updated = new SingletonPredictionContext(parents[0], context.getReturnState(0));
} else {
let returnStates: number[] = new Array<number>(context.size);
for (let i = 0; i < context.size; i++) {
returnStates[i] = context.getReturnState(i);
}
updated = new ArrayPredictionContext(parents, returnStates, context.hashCode());
}
existing = contextCache.putIfAbsent(updated, updated);
visited.put(updated, existing || updated);
visited.put(context, existing || updated);
return updated;
}
public appendSingleContext(returnContext: number, contextCache: PredictionContextCache): PredictionContext {
return this.appendContext(PredictionContext.EMPTY_FULL.getChild(returnContext), contextCache);
}
public abstract appendContext(suffix: PredictionContext, contextCache: PredictionContextCache): PredictionContext;
public getChild(returnState: number): PredictionContext {
return new SingletonPredictionContext(this, returnState);
}
public abstract readonly isEmpty: boolean;
public abstract readonly hasEmpty: boolean;
public hashCode(): number {
return this.cachedHashCode;
}
// @Override
public abstract equals(o: any): boolean;
public toStrings(recognizer: Recognizer<any, any> | undefined, currentState: number, stop: PredictionContext = PredictionContext.EMPTY_FULL): string[] {
let result: string[] = [];
outer:
for (let perm = 0; ; perm++) {
let offset: number = 0;
let last: boolean = true;
let p: PredictionContext = this;
let stateNumber: number = currentState;
let localBuffer: string = "";
localBuffer += "[";
while (!p.isEmpty && p !== stop) {
let index: number = 0;
if (p.size > 0) {
let bits: number = 1;
while (((1 << bits) >>> 0) < p.size) {
bits++;
}
let mask: number = ((1 << bits) >>> 0) - 1;
index = (perm >> offset) & mask;
last = last && index >= p.size - 1;
if (index >= p.size) {
continue outer;
}
offset += bits;
}
if (recognizer) {
if (localBuffer.length > 1) {
// first char is '[', if more than that this isn't the first rule
localBuffer += " ";
}
let atn: ATN = recognizer.atn;
let s: ATNState = atn.states[stateNumber];
let ruleName: string = recognizer.ruleNames[s.ruleIndex];
localBuffer += ruleName;
} else if (p.getReturnState(index) !== PredictionContext.EMPTY_FULL_STATE_KEY) {
if (!p.isEmpty) {
if (localBuffer.length > 1) {
// first char is '[', if more than that this isn't the first rule
localBuffer += " ";
}
localBuffer += p.getReturnState(index);
}
}
stateNumber = p.getReturnState(index);
p = p.getParent(index);
}
localBuffer += "]";
result.push(localBuffer);
if (last) {
break;
}
}
return result;
}
}
class EmptyPredictionContext extends PredictionContext {
private fullContext: boolean;
constructor(fullContext: boolean) {
super(PredictionContext.calculateEmptyHashCode());
this.fullContext = fullContext;
}
get isFullContext(): boolean {
return this.fullContext;
}
protected addEmptyContext(): PredictionContext {
return this;
}
protected removeEmptyContext(): PredictionContext {
throw new Error("Cannot remove the empty context from itself.");
}
public getParent(index: number): PredictionContext {
throw new Error("index out of bounds");
}
public getReturnState(index: number): number {
throw new Error("index out of bounds");
}
public findReturnState(returnState: number): number {
return -1;
}
get size(): number {
return 0;
}
public appendSingleContext(returnContext: number, contextCache: PredictionContextCache): PredictionContext {
return contextCache.getChild(this, returnContext);
}
public appendContext(suffix: PredictionContext, contextCache: PredictionContextCache): PredictionContext {
return suffix;
}
get isEmpty(): boolean {
return true;
}
get hasEmpty(): boolean {
return true;
}
public equals(o: any): boolean {
return this === o;
}
public toStrings(recognizer: any, currentState: number, stop?: PredictionContext): string[] {
return ["[]"];
}
}
class ArrayPredictionContext extends PredictionContext {
public parents: PredictionContext[];
public returnStates: number[];
constructor( parents: PredictionContext[], returnStates: number[], hashCode?: number) {
super(hashCode || PredictionContext.calculateHashCode(parents, returnStates));
assert(parents.length === returnStates.length);
assert(returnStates.length > 1 || returnStates[0] !== PredictionContext.EMPTY_FULL_STATE_KEY, "Should be using PredictionContext.EMPTY instead.");
this.parents = parents;
this.returnStates = returnStates;
}
public getParent(index: number): PredictionContext {
return this.parents[index];
}
public getReturnState(index: number): number {
return this.returnStates[index];
}
public findReturnState(returnState: number): number {
return Arrays.binarySearch(this.returnStates, returnState);
}
get size(): number {
return this.returnStates.length;
}
get isEmpty(): boolean {
return false;
}
get hasEmpty(): boolean {
return this.returnStates[this.returnStates.length - 1] === PredictionContext.EMPTY_FULL_STATE_KEY;
}
protected addEmptyContext(): PredictionContext {
if (this.hasEmpty) {
return this;
}
let parents2: PredictionContext[] = this.parents.slice(0);
let returnStates2: number[] = this.returnStates.slice(0);
parents2.push(PredictionContext.EMPTY_FULL);
returnStates2.push(PredictionContext.EMPTY_FULL_STATE_KEY);
return new ArrayPredictionContext(parents2, returnStates2);
}
protected removeEmptyContext(): PredictionContext {
if (!this.hasEmpty) {
return this;
}
if (this.returnStates.length === 2) {
return new SingletonPredictionContext(this.parents[0], this.returnStates[0]);
} else {
let parents2: PredictionContext[] = this.parents.slice(0, this.parents.length - 1);
let returnStates2: number[] = this.returnStates.slice(0, this.returnStates.length - 1);
return new ArrayPredictionContext(parents2, returnStates2);
}
}
public appendContext(suffix: PredictionContext, contextCache: PredictionContextCache): PredictionContext {
return ArrayPredictionContext.appendContextImpl(this, suffix, new PredictionContext.IdentityHashMap());
}
private static appendContextImpl(context: PredictionContext, suffix: PredictionContext, visited: PredictionContext.IdentityHashMap): PredictionContext {
if (suffix.isEmpty) {
if (PredictionContext.isEmptyLocal(suffix)) {
if (context.hasEmpty) {
return PredictionContext.EMPTY_LOCAL;
}
throw new Error("what to do here?");
}
return context;
}
if (suffix.size !== 1) {
throw new Error("Appending a tree suffix is not yet supported.");
}
let result = visited.get(context);
if (!result) {
if (context.isEmpty) {
result = suffix;
} else {
let parentCount: number = context.size;
if (context.hasEmpty) {
parentCount--;
}
let updatedParents: PredictionContext[] = new Array<PredictionContext>(parentCount);
let updatedReturnStates: number[] = new Array<number>(parentCount);
for (let i = 0; i < parentCount; i++) {
updatedReturnStates[i] = context.getReturnState(i);
}
for (let i = 0; i < parentCount; i++) {
updatedParents[i] = ArrayPredictionContext.appendContextImpl(context.getParent(i), suffix, visited);
}
if (updatedParents.length === 1) {
result = new SingletonPredictionContext(updatedParents[0], updatedReturnStates[0]);
} else {
assert(updatedParents.length > 1);
result = new ArrayPredictionContext(updatedParents, updatedReturnStates);
}
if (context.hasEmpty) {
result = PredictionContext.join(result, suffix);
}
}
visited.put(context, result);
}
return result;
}
public equals(o: any): boolean {
if (this === o) {
return true;
} else if (!(o instanceof ArrayPredictionContext)) {
return false;
}
if (this.hashCode() !== o.hashCode()) {
// can't be same if hash is different
return false;
}
let other: ArrayPredictionContext = o;
return this.equalsImpl(other, new Array2DHashSet<PredictionContextCache.IdentityCommutativePredictionContextOperands>());
}
private equalsImpl(other: ArrayPredictionContext, visited: JavaSet<PredictionContextCache.IdentityCommutativePredictionContextOperands>): boolean {
let selfWorkList: PredictionContext[] = [];
let otherWorkList: PredictionContext[] = [];
selfWorkList.push(this);
otherWorkList.push(other);
while (true) {
let currentSelf = selfWorkList.pop();
let currentOther = otherWorkList.pop();
if (!currentSelf || !currentOther) {
break;
}
let operands: PredictionContextCache.IdentityCommutativePredictionContextOperands = new PredictionContextCache.IdentityCommutativePredictionContextOperands(currentSelf, currentOther);
if (!visited.add(operands)) {
continue;
}
let selfSize: number = operands.x.size;
if (selfSize === 0) {
if (!operands.x.equals(operands.y)) {
return false;
}
continue;
}
let otherSize: number = operands.y.size;
if (selfSize !== otherSize) {
return false;
}
for (let i = 0; i < selfSize; i++) {
if (operands.x.getReturnState(i) !== operands.y.getReturnState(i)) {
return false;
}
let selfParent: PredictionContext = operands.x.getParent(i);
let otherParent: PredictionContext = operands.y.getParent(i);
if (selfParent.hashCode() !== otherParent.hashCode()) {
return false;
}
if (selfParent !== otherParent) {
selfWorkList.push(selfParent);
otherWorkList.push(otherParent);
}
}
}
return true;
}
}
export class SingletonPredictionContext extends PredictionContext {
public parent: PredictionContext;
public returnState: number;
constructor( parent: PredictionContext, returnState: number) {
super(PredictionContext.calculateSingleHashCode(parent, returnState));
// assert(returnState != PredictionContext.EMPTY_FULL_STATE_KEY && returnState != PredictionContext.EMPTY_LOCAL_STATE_KEY);
this.parent = parent;
this.returnState = returnState;
}
public getParent(index: number): PredictionContext {
// assert(index == 0);
return this.parent;
}
public getReturnState(index: number): number {
// assert(index == 0);
return this.returnState;
}
public findReturnState(returnState: number): number {
return this.returnState === returnState ? 0 : -1;
}
get size(): number {
return 1;
}
get isEmpty(): boolean {
return false;
}
get hasEmpty(): boolean {
return false;
}
public appendContext(suffix: PredictionContext, contextCache: PredictionContextCache): PredictionContext {
return contextCache.getChild(this.parent.appendContext(suffix, contextCache), this.returnState);
}
protected addEmptyContext(): PredictionContext {
let parents: PredictionContext[] = [this.parent, PredictionContext.EMPTY_FULL];
let returnStates: number[] = [this.returnState, PredictionContext.EMPTY_FULL_STATE_KEY];
return new ArrayPredictionContext(parents, returnStates);
}
protected removeEmptyContext(): PredictionContext {
return this;
}
public equals(o: any): boolean {
if (o === this) {
return true;
} else if (!(o instanceof SingletonPredictionContext)) {
return false;
}
let other: SingletonPredictionContext = o;
if (this.hashCode() !== other.hashCode()) {
return false;
}
return this.returnState === other.returnState
&& this.parent.equals(other.parent);
}
}
export namespace PredictionContext {
export const EMPTY_LOCAL: PredictionContext = new EmptyPredictionContext(false);
export const EMPTY_FULL: PredictionContext = new EmptyPredictionContext(true);
export const EMPTY_LOCAL_STATE_KEY: number = -((1 << 31) >>> 0);
export const EMPTY_FULL_STATE_KEY: number = ((1 << 31) >>> 0) - 1;
export class IdentityHashMap extends Array2DHashMap<PredictionContext, PredictionContext> {
constructor() {
super(IdentityEqualityComparator.INSTANCE);
}
}
export class IdentityEqualityComparator implements EqualityComparator<PredictionContext> {
public static readonly INSTANCE: IdentityEqualityComparator = new IdentityEqualityComparator();
private IdentityEqualityComparator() {
// intentionally empty
}
public hashCode(obj: PredictionContext): number {
return obj.hashCode();
}
public equals(a: PredictionContext, b: PredictionContext): boolean {
return a === b;
}
}
}