UNPKG

bitmark-grammar

Version:
757 lines (617 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: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 &lt; this.size; i++) { * hash = {@link MurmurHash#update MurmurHash.update}(hash, {@link #getParent getParent}(i)); * } * * for (int i = 0; i &lt; 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(@NotNull context0: PredictionContext, @NotNull context1: PredictionContext, @NotNull 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( @NotNull context: PredictionContext, @NotNull contextCache: Array2DHashMap<PredictionContext, PredictionContext>, @NotNull 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; @Override 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; } @Override protected addEmptyContext(): PredictionContext { return this; } @Override protected removeEmptyContext(): PredictionContext { throw new Error("Cannot remove the empty context from itself."); } @Override public getParent(index: number): PredictionContext { throw new Error("index out of bounds"); } @Override public getReturnState(index: number): number { throw new Error("index out of bounds"); } @Override public findReturnState(returnState: number): number { return -1; } @Override get size(): number { return 0; } @Override public appendSingleContext(returnContext: number, contextCache: PredictionContextCache): PredictionContext { return contextCache.getChild(this, returnContext); } @Override public appendContext(suffix: PredictionContext, contextCache: PredictionContextCache): PredictionContext { return suffix; } @Override get isEmpty(): boolean { return true; } @Override get hasEmpty(): boolean { return true; } @Override public equals(o: any): boolean { return this === o; } @Override public toStrings(recognizer: any, currentState: number, stop?: PredictionContext): string[] { return ["[]"]; } } class ArrayPredictionContext extends PredictionContext { @NotNull public parents: PredictionContext[]; @NotNull public returnStates: number[]; constructor( @NotNull 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; } @Override public getParent(index: number): PredictionContext { return this.parents[index]; } @Override public getReturnState(index: number): number { return this.returnStates[index]; } @Override public findReturnState(returnState: number): number { return Arrays.binarySearch(this.returnStates, returnState); } @Override get size(): number { return this.returnStates.length; } @Override get isEmpty(): boolean { return false; } @Override get hasEmpty(): boolean { return this.returnStates[this.returnStates.length - 1] === PredictionContext.EMPTY_FULL_STATE_KEY; } @Override 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); } @Override 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); } } @Override 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; } @Override 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 { @NotNull public parent: PredictionContext; public returnState: number; constructor(@NotNull 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; } @Override public getParent(index: number): PredictionContext { // assert(index == 0); return this.parent; } @Override public getReturnState(index: number): number { // assert(index == 0); return this.returnState; } @Override public findReturnState(returnState: number): number { return this.returnState === returnState ? 0 : -1; } @Override get size(): number { return 1; } @Override get isEmpty(): boolean { return false; } @Override get hasEmpty(): boolean { return false; } @Override public appendContext(suffix: PredictionContext, contextCache: PredictionContextCache): PredictionContext { return contextCache.getChild(this.parent.appendContext(suffix, contextCache), this.returnState); } @Override 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); } @Override protected removeEmptyContext(): PredictionContext { return this; } @Override 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 } @Override public hashCode(obj: PredictionContext): number { return obj.hashCode(); } @Override public equals(a: PredictionContext, b: PredictionContext): boolean { return a === b; } } }