refactored PredictionContext.js to use es6 classes
use const/let for better scoping use jsdoc
This commit is contained in:
parent
ee9474b194
commit
5baa2326ad
|
@ -1,292 +1,293 @@
|
|||
//
|
||||
/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
|
||||
* Use of this file is governed by the BSD 3-clause license that
|
||||
* can be found in the LICENSE.txt file in the project root.
|
||||
*/
|
||||
///
|
||||
|
||||
var RuleContext = require('./RuleContext');
|
||||
var Hash = require('./Utils').Hash;
|
||||
var Map = require('./Utils').Map;
|
||||
const RuleContext = require('./RuleContext');
|
||||
const {Hash, Map} = require('./Utils');
|
||||
|
||||
function PredictionContext(cachedHashCode) {
|
||||
this.cachedHashCode = cachedHashCode;
|
||||
class PredictionContext {
|
||||
constructor(cachedHashCode) {
|
||||
this.cachedHashCode = cachedHashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the computed hash code of this {@link PredictionContext}. The hash
|
||||
* code is computed in parts to match the following reference algorithm.
|
||||
*
|
||||
* <pre>
|
||||
* private int referenceHashCode() {
|
||||
* int hash = {@link MurmurHash//initialize MurmurHash.initialize}({@link
|
||||
* //INITIAL_HASH});
|
||||
*
|
||||
* for (int i = 0; i < {@link //size()}; i++) {
|
||||
* hash = {@link MurmurHash//update MurmurHash.update}(hash, {@link //getParent
|
||||
* getParent}(i));
|
||||
* }
|
||||
*
|
||||
* for (int i = 0; i < {@link //size()}; i++) {
|
||||
* hash = {@link MurmurHash//update MurmurHash.update}(hash, {@link
|
||||
* //getReturnState getReturnState}(i));
|
||||
* }
|
||||
*
|
||||
* hash = {@link MurmurHash//finish MurmurHash.finish}(hash, 2// {@link
|
||||
* //size()});
|
||||
* return hash;
|
||||
* }
|
||||
* </pre>
|
||||
* This means only the {@link //EMPTY} context is in set.
|
||||
*/
|
||||
isEmpty() {
|
||||
return this === PredictionContext.EMPTY;
|
||||
}
|
||||
|
||||
hasEmptyPath() {
|
||||
return this.getReturnState(this.length - 1) === PredictionContext.EMPTY_RETURN_STATE;
|
||||
}
|
||||
|
||||
hashCode() {
|
||||
return this.cachedHashCode;
|
||||
}
|
||||
|
||||
updateHashCode(hash) {
|
||||
hash.update(this.cachedHashCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Represents {@code $} in local context prediction, which means wildcard.
|
||||
// {@code//+x =//}.
|
||||
// /
|
||||
/**
|
||||
* Represents {@code $} in local context prediction, which means wildcard.
|
||||
* {@code//+x =//}.
|
||||
*/
|
||||
PredictionContext.EMPTY = null;
|
||||
|
||||
// Represents {@code $} in an array in full context mode, when {@code $}
|
||||
// doesn't mean wildcard: {@code $ + x = [$,x]}. Here,
|
||||
// {@code $} = {@link //EMPTY_RETURN_STATE}.
|
||||
// /
|
||||
/**
|
||||
* Represents {@code $} in an array in full context mode, when {@code $}
|
||||
* doesn't mean wildcard: {@code $ + x = [$,x]}. Here,
|
||||
* {@code $} = {@link //EMPTY_RETURN_STATE}.
|
||||
*/
|
||||
PredictionContext.EMPTY_RETURN_STATE = 0x7FFFFFFF;
|
||||
|
||||
PredictionContext.globalNodeCount = 1;
|
||||
PredictionContext.id = PredictionContext.globalNodeCount;
|
||||
|
||||
// Stores the computed hash code of this {@link PredictionContext}. The hash
|
||||
// code is computed in parts to match the following reference algorithm.
|
||||
//
|
||||
// <pre>
|
||||
// private int referenceHashCode() {
|
||||
// int hash = {@link MurmurHash//initialize MurmurHash.initialize}({@link
|
||||
// //INITIAL_HASH});
|
||||
//
|
||||
// for (int i = 0; i < {@link //size()}; i++) {
|
||||
// hash = {@link MurmurHash//update MurmurHash.update}(hash, {@link //getParent
|
||||
// getParent}(i));
|
||||
// }
|
||||
//
|
||||
// for (int i = 0; i < {@link //size()}; i++) {
|
||||
// hash = {@link MurmurHash//update MurmurHash.update}(hash, {@link
|
||||
// //getReturnState getReturnState}(i));
|
||||
// }
|
||||
//
|
||||
// hash = {@link MurmurHash//finish MurmurHash.finish}(hash, 2// {@link
|
||||
// //size()});
|
||||
// return hash;
|
||||
// }
|
||||
// </pre>
|
||||
// /
|
||||
|
||||
// This means only the {@link //EMPTY} context is in set.
|
||||
PredictionContext.prototype.isEmpty = function() {
|
||||
return this === PredictionContext.EMPTY;
|
||||
};
|
||||
|
||||
PredictionContext.prototype.hasEmptyPath = function() {
|
||||
return this.getReturnState(this.length - 1) === PredictionContext.EMPTY_RETURN_STATE;
|
||||
};
|
||||
|
||||
PredictionContext.prototype.hashCode = function() {
|
||||
return this.cachedHashCode;
|
||||
};
|
||||
|
||||
|
||||
PredictionContext.prototype.updateHashCode = function(hash) {
|
||||
hash.update(this.cachedHashCode);
|
||||
};
|
||||
/*
|
||||
function calculateHashString(parent, returnState) {
|
||||
return "" + parent + returnState;
|
||||
}
|
||||
*/
|
||||
|
||||
// Used to cache {@link PredictionContext} objects. Its used for the shared
|
||||
// context cash associated with contexts in DFA states. This cache
|
||||
// can be used for both lexers and parsers.
|
||||
|
||||
function PredictionContextCache() {
|
||||
this.cache = new Map();
|
||||
return this;
|
||||
}
|
||||
|
||||
// Add a context to the cache and return it. If the context already exists,
|
||||
// return that one instead and do not add a new context to the cache.
|
||||
// Protect shared cache from unsafe thread access.
|
||||
//
|
||||
PredictionContextCache.prototype.add = function(ctx) {
|
||||
if (ctx === PredictionContext.EMPTY) {
|
||||
return PredictionContext.EMPTY;
|
||||
/**
|
||||
* Used to cache {@link PredictionContext} objects. Its used for the shared
|
||||
* context cash associated with contexts in DFA states. This cache
|
||||
* can be used for both lexers and parsers.
|
||||
*/
|
||||
class PredictionContextCache {
|
||||
constructor() {
|
||||
this.cache = new Map();
|
||||
}
|
||||
var existing = this.cache.get(ctx) || null;
|
||||
if (existing !== null) {
|
||||
return existing;
|
||||
|
||||
/**
|
||||
* Add a context to the cache and return it. If the context already exists,
|
||||
* return that one instead and do not add a new context to the cache.
|
||||
* Protect shared cache from unsafe thread access.
|
||||
*/
|
||||
add(ctx) {
|
||||
if (ctx === PredictionContext.EMPTY) {
|
||||
return PredictionContext.EMPTY;
|
||||
}
|
||||
const existing = this.cache.get(ctx) || null;
|
||||
if (existing !== null) {
|
||||
return existing;
|
||||
}
|
||||
this.cache.put(ctx, ctx);
|
||||
return ctx;
|
||||
}
|
||||
this.cache.put(ctx, ctx);
|
||||
return ctx;
|
||||
};
|
||||
|
||||
PredictionContextCache.prototype.get = function(ctx) {
|
||||
return this.cache.get(ctx) || null;
|
||||
};
|
||||
get(ctx) {
|
||||
return this.cache.get(ctx) || null;
|
||||
}
|
||||
|
||||
Object.defineProperty(PredictionContextCache.prototype, "length", {
|
||||
get : function() {
|
||||
get length(){
|
||||
return this.cache.length;
|
||||
}
|
||||
});
|
||||
|
||||
function SingletonPredictionContext(parent, returnState) {
|
||||
var hashCode = 0;
|
||||
var hash = new Hash();
|
||||
if(parent !== null) {
|
||||
hash.update(parent, returnState);
|
||||
} else {
|
||||
hash.update(1);
|
||||
}
|
||||
hashCode = hash.finish();
|
||||
PredictionContext.call(this, hashCode);
|
||||
this.parentCtx = parent;
|
||||
this.returnState = returnState;
|
||||
}
|
||||
|
||||
SingletonPredictionContext.prototype = Object.create(PredictionContext.prototype);
|
||||
SingletonPredictionContext.prototype.contructor = SingletonPredictionContext;
|
||||
|
||||
SingletonPredictionContext.create = function(parent, returnState) {
|
||||
if (returnState === PredictionContext.EMPTY_RETURN_STATE && parent === null) {
|
||||
// someone can pass in the bits of an array ctx that mean $
|
||||
return PredictionContext.EMPTY;
|
||||
} else {
|
||||
return new SingletonPredictionContext(parent, returnState);
|
||||
class SingletonPredictionContext extends PredictionContext {
|
||||
constructor(parent, returnState) {
|
||||
let hashCode = 0;
|
||||
const hash = new Hash();
|
||||
if(parent !== null) {
|
||||
hash.update(parent, returnState);
|
||||
} else {
|
||||
hash.update(1);
|
||||
}
|
||||
hashCode = hash.finish();
|
||||
super(hashCode);
|
||||
this.parentCtx = parent;
|
||||
this.returnState = returnState;
|
||||
}
|
||||
};
|
||||
|
||||
Object.defineProperty(SingletonPredictionContext.prototype, "length", {
|
||||
get : function() {
|
||||
getParent(index) {
|
||||
return this.parentCtx;
|
||||
}
|
||||
|
||||
getReturnState(index) {
|
||||
return this.returnState;
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
if (this === other) {
|
||||
return true;
|
||||
} else if (!(other instanceof SingletonPredictionContext)) {
|
||||
return false;
|
||||
} else if (this.hashCode() !== other.hashCode()) {
|
||||
return false; // can't be same if hash is different
|
||||
} else {
|
||||
if(this.returnState !== other.returnState)
|
||||
return false;
|
||||
else if(this.parentCtx==null)
|
||||
return other.parentCtx==null
|
||||
else
|
||||
return this.parentCtx.equals(other.parentCtx);
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
const up = this.parentCtx === null ? "" : this.parentCtx.toString();
|
||||
if (up.length === 0) {
|
||||
if (this.returnState === PredictionContext.EMPTY_RETURN_STATE) {
|
||||
return "$";
|
||||
} else {
|
||||
return "" + this.returnState;
|
||||
}
|
||||
} else {
|
||||
return "" + this.returnState + " " + up;
|
||||
}
|
||||
}
|
||||
|
||||
get length(){
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
SingletonPredictionContext.prototype.getParent = function(index) {
|
||||
return this.parentCtx;
|
||||
};
|
||||
|
||||
SingletonPredictionContext.prototype.getReturnState = function(index) {
|
||||
return this.returnState;
|
||||
};
|
||||
|
||||
SingletonPredictionContext.prototype.equals = function(other) {
|
||||
if (this === other) {
|
||||
return true;
|
||||
} else if (!(other instanceof SingletonPredictionContext)) {
|
||||
return false;
|
||||
} else if (this.hashCode() !== other.hashCode()) {
|
||||
return false; // can't be same if hash is different
|
||||
} else {
|
||||
if(this.returnState !== other.returnState)
|
||||
return false;
|
||||
else if(this.parentCtx==null)
|
||||
return other.parentCtx==null
|
||||
else
|
||||
return this.parentCtx.equals(other.parentCtx);
|
||||
}
|
||||
};
|
||||
|
||||
SingletonPredictionContext.prototype.toString = function() {
|
||||
var up = this.parentCtx === null ? "" : this.parentCtx.toString();
|
||||
if (up.length === 0) {
|
||||
if (this.returnState === PredictionContext.EMPTY_RETURN_STATE) {
|
||||
return "$";
|
||||
static create(parent, returnState) {
|
||||
if (returnState === PredictionContext.EMPTY_RETURN_STATE && parent === null) {
|
||||
// someone can pass in the bits of an array ctx that mean $
|
||||
return PredictionContext.EMPTY;
|
||||
} else {
|
||||
return "" + this.returnState;
|
||||
return new SingletonPredictionContext(parent, returnState);
|
||||
}
|
||||
} else {
|
||||
return "" + this.returnState + " " + up;
|
||||
}
|
||||
};
|
||||
|
||||
function EmptyPredictionContext() {
|
||||
SingletonPredictionContext.call(this, null, PredictionContext.EMPTY_RETURN_STATE);
|
||||
return this;
|
||||
}
|
||||
|
||||
EmptyPredictionContext.prototype = Object.create(SingletonPredictionContext.prototype);
|
||||
EmptyPredictionContext.prototype.constructor = EmptyPredictionContext;
|
||||
class EmptyPredictionContext extends SingletonPredictionContext {
|
||||
constructor() {
|
||||
super(null, PredictionContext.EMPTY_RETURN_STATE);
|
||||
}
|
||||
|
||||
EmptyPredictionContext.prototype.isEmpty = function() {
|
||||
return true;
|
||||
};
|
||||
isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
EmptyPredictionContext.prototype.getParent = function(index) {
|
||||
return null;
|
||||
};
|
||||
getParent(index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
EmptyPredictionContext.prototype.getReturnState = function(index) {
|
||||
return this.returnState;
|
||||
};
|
||||
getReturnState(index) {
|
||||
return this.returnState;
|
||||
}
|
||||
|
||||
EmptyPredictionContext.prototype.equals = function(other) {
|
||||
return this === other;
|
||||
};
|
||||
equals(other) {
|
||||
return this === other;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "$";
|
||||
}
|
||||
}
|
||||
|
||||
EmptyPredictionContext.prototype.toString = function() {
|
||||
return "$";
|
||||
};
|
||||
|
||||
PredictionContext.EMPTY = new EmptyPredictionContext();
|
||||
|
||||
function ArrayPredictionContext(parents, returnStates) {
|
||||
// Parent can be null only if full ctx mode and we make an array
|
||||
// from {@link //EMPTY} and non-empty. We merge {@link //EMPTY} by using
|
||||
// null parent and
|
||||
// returnState == {@link //EMPTY_RETURN_STATE}.
|
||||
var h = new Hash();
|
||||
h.update(parents, returnStates);
|
||||
var hashCode = h.finish();
|
||||
PredictionContext.call(this, hashCode);
|
||||
this.parents = parents;
|
||||
this.returnStates = returnStates;
|
||||
return this;
|
||||
}
|
||||
class ArrayPredictionContext extends PredictionContext {
|
||||
constructor(parents, returnStates) {
|
||||
/**
|
||||
* Parent can be null only if full ctx mode and we make an array
|
||||
* from {@link //EMPTY} and non-empty. We merge {@link //EMPTY} by using
|
||||
* null parent and
|
||||
* returnState == {@link //EMPTY_RETURN_STATE}.
|
||||
*/
|
||||
const h = new Hash();
|
||||
h.update(parents, returnStates);
|
||||
const hashCode = h.finish();
|
||||
super(hashCode);
|
||||
this.parents = parents;
|
||||
this.returnStates = returnStates;
|
||||
return this;
|
||||
}
|
||||
|
||||
ArrayPredictionContext.prototype = Object.create(PredictionContext.prototype);
|
||||
ArrayPredictionContext.prototype.constructor = ArrayPredictionContext;
|
||||
isEmpty() {
|
||||
// since EMPTY_RETURN_STATE can only appear in the last position, we
|
||||
// don't need to verify that size==1
|
||||
return this.returnStates[0] === PredictionContext.EMPTY_RETURN_STATE;
|
||||
}
|
||||
|
||||
ArrayPredictionContext.prototype.isEmpty = function() {
|
||||
// since EMPTY_RETURN_STATE can only appear in the last position, we
|
||||
// don't need to verify that size==1
|
||||
return this.returnStates[0] === PredictionContext.EMPTY_RETURN_STATE;
|
||||
};
|
||||
getParent(index) {
|
||||
return this.parents[index];
|
||||
}
|
||||
|
||||
Object.defineProperty(ArrayPredictionContext.prototype, "length", {
|
||||
get : function() {
|
||||
getReturnState(index) {
|
||||
return this.returnStates[index];
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
if (this === other) {
|
||||
return true;
|
||||
} else if (!(other instanceof ArrayPredictionContext)) {
|
||||
return false;
|
||||
} else if (this.hashCode() !== other.hashCode()) {
|
||||
return false; // can't be same if hash is different
|
||||
} else {
|
||||
return this.returnStates === other.returnStates &&
|
||||
this.parents === other.parents;
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (this.isEmpty()) {
|
||||
return "[]";
|
||||
} else {
|
||||
let s = "[";
|
||||
for (let i = 0; i < this.returnStates.length; i++) {
|
||||
if (i > 0) {
|
||||
s = s + ", ";
|
||||
}
|
||||
if (this.returnStates[i] === PredictionContext.EMPTY_RETURN_STATE) {
|
||||
s = s + "$";
|
||||
continue;
|
||||
}
|
||||
s = s + this.returnStates[i];
|
||||
if (this.parents[i] !== null) {
|
||||
s = s + " " + this.parents[i];
|
||||
} else {
|
||||
s = s + "null";
|
||||
}
|
||||
}
|
||||
return s + "]";
|
||||
}
|
||||
}
|
||||
|
||||
get length(){
|
||||
return this.returnStates.length;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ArrayPredictionContext.prototype.getParent = function(index) {
|
||||
return this.parents[index];
|
||||
};
|
||||
|
||||
ArrayPredictionContext.prototype.getReturnState = function(index) {
|
||||
return this.returnStates[index];
|
||||
};
|
||||
|
||||
ArrayPredictionContext.prototype.equals = function(other) {
|
||||
if (this === other) {
|
||||
return true;
|
||||
} else if (!(other instanceof ArrayPredictionContext)) {
|
||||
return false;
|
||||
} else if (this.hashCode() !== other.hashCode()) {
|
||||
return false; // can't be same if hash is different
|
||||
} else {
|
||||
return this.returnStates === other.returnStates &&
|
||||
this.parents === other.parents;
|
||||
}
|
||||
};
|
||||
|
||||
ArrayPredictionContext.prototype.toString = function() {
|
||||
if (this.isEmpty()) {
|
||||
return "[]";
|
||||
} else {
|
||||
var s = "[";
|
||||
for (var i = 0; i < this.returnStates.length; i++) {
|
||||
if (i > 0) {
|
||||
s = s + ", ";
|
||||
}
|
||||
if (this.returnStates[i] === PredictionContext.EMPTY_RETURN_STATE) {
|
||||
s = s + "$";
|
||||
continue;
|
||||
}
|
||||
s = s + this.returnStates[i];
|
||||
if (this.parents[i] !== null) {
|
||||
s = s + " " + this.parents[i];
|
||||
} else {
|
||||
s = s + "null";
|
||||
}
|
||||
}
|
||||
return s + "]";
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a {@link RuleContext} tree to a {@link PredictionContext} graph.
|
||||
// Return {@link //EMPTY} if {@code outerContext} is empty or null.
|
||||
// /
|
||||
/**
|
||||
* Convert a {@link RuleContext} tree to a {@link PredictionContext} graph.
|
||||
* Return {@link //EMPTY} if {@code outerContext} is empty or null.
|
||||
*/
|
||||
function predictionContextFromRuleContext(atn, outerContext) {
|
||||
if (outerContext === undefined || outerContext === null) {
|
||||
outerContext = RuleContext.EMPTY;
|
||||
|
@ -297,14 +298,14 @@ function predictionContextFromRuleContext(atn, outerContext) {
|
|||
return PredictionContext.EMPTY;
|
||||
}
|
||||
// If we have a parent, convert it to a PredictionContext graph
|
||||
var parent = predictionContextFromRuleContext(atn, outerContext.parentCtx);
|
||||
var state = atn.states[outerContext.invokingState];
|
||||
var transition = state.transitions[0];
|
||||
const parent = predictionContextFromRuleContext(atn, outerContext.parentCtx);
|
||||
const state = atn.states[outerContext.invokingState];
|
||||
const transition = state.transitions[0];
|
||||
return SingletonPredictionContext.create(parent, transition.followState.stateNumber);
|
||||
}
|
||||
/*
|
||||
function calculateListsHashString(parents, returnStates) {
|
||||
var s = "";
|
||||
const s = "";
|
||||
parents.map(function(p) {
|
||||
s = s + p;
|
||||
});
|
||||
|
@ -342,40 +343,40 @@ function merge(a, b, rootIsWildcard, mergeCache) {
|
|||
return mergeArrays(a, b, rootIsWildcard, mergeCache);
|
||||
}
|
||||
|
||||
//
|
||||
// Merge two {@link SingletonPredictionContext} instances.
|
||||
//
|
||||
// <p>Stack tops equal, parents merge is same; return left graph.<br>
|
||||
// <embed src="images/SingletonMerge_SameRootSamePar.svg"
|
||||
// type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Same stack top, parents differ; merge parents giving array node, then
|
||||
// remainders of those graphs. A new root node is created to point to the
|
||||
// merged parents.<br>
|
||||
// <embed src="images/SingletonMerge_SameRootDiffPar.svg"
|
||||
// type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Different stack tops pointing to same parent. Make array node for the
|
||||
// root where both element in the root point to the same (original)
|
||||
// parent.<br>
|
||||
// <embed src="images/SingletonMerge_DiffRootSamePar.svg"
|
||||
// type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Different stack tops pointing to different parents. Make array node for
|
||||
// the root where each element points to the corresponding original
|
||||
// parent.<br>
|
||||
// <embed src="images/SingletonMerge_DiffRootDiffPar.svg"
|
||||
// type="image/svg+xml"/></p>
|
||||
//
|
||||
// @param a the first {@link SingletonPredictionContext}
|
||||
// @param b the second {@link SingletonPredictionContext}
|
||||
// @param rootIsWildcard {@code true} if this is a local-context merge,
|
||||
// otherwise false to indicate a full-context merge
|
||||
// @param mergeCache
|
||||
// /
|
||||
/**
|
||||
* Merge two {@link SingletonPredictionContext} instances.
|
||||
*
|
||||
* <p>Stack tops equal, parents merge is same; return left graph.<br>
|
||||
* <embed src="images/SingletonMerge_SameRootSamePar.svg"
|
||||
* type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Same stack top, parents differ; merge parents giving array node, then
|
||||
* remainders of those graphs. A new root node is created to point to the
|
||||
* merged parents.<br>
|
||||
* <embed src="images/SingletonMerge_SameRootDiffPar.svg"
|
||||
* type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Different stack tops pointing to same parent. Make array node for the
|
||||
* root where both element in the root point to the same (original)
|
||||
* parent.<br>
|
||||
* <embed src="images/SingletonMerge_DiffRootSamePar.svg"
|
||||
* type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Different stack tops pointing to different parents. Make array node for
|
||||
* the root where each element points to the corresponding original
|
||||
* parent.<br>
|
||||
* <embed src="images/SingletonMerge_DiffRootDiffPar.svg"
|
||||
* type="image/svg+xml"/></p>
|
||||
*
|
||||
* @param a the first {@link SingletonPredictionContext}
|
||||
* @param b the second {@link SingletonPredictionContext}
|
||||
* @param rootIsWildcard {@code true} if this is a local-context merge,
|
||||
* otherwise false to indicate a full-context merge
|
||||
* @param mergeCache
|
||||
*/
|
||||
function mergeSingletons(a, b, rootIsWildcard, mergeCache) {
|
||||
if (mergeCache !== null) {
|
||||
var previous = mergeCache.get(a, b);
|
||||
let previous = mergeCache.get(a, b);
|
||||
if (previous !== null) {
|
||||
return previous;
|
||||
}
|
||||
|
@ -385,7 +386,7 @@ function mergeSingletons(a, b, rootIsWildcard, mergeCache) {
|
|||
}
|
||||
}
|
||||
|
||||
var rootMerge = mergeRoot(a, b, rootIsWildcard);
|
||||
const rootMerge = mergeRoot(a, b, rootIsWildcard);
|
||||
if (rootMerge !== null) {
|
||||
if (mergeCache !== null) {
|
||||
mergeCache.set(a, b, rootMerge);
|
||||
|
@ -393,7 +394,7 @@ function mergeSingletons(a, b, rootIsWildcard, mergeCache) {
|
|||
return rootMerge;
|
||||
}
|
||||
if (a.returnState === b.returnState) {
|
||||
var parent = merge(a.parentCtx, b.parentCtx, rootIsWildcard, mergeCache);
|
||||
const parent = merge(a.parentCtx, b.parentCtx, rootIsWildcard, mergeCache);
|
||||
// if parent is same as existing a or b parent or reduced to a parent,
|
||||
// return it
|
||||
if (parent === a.parentCtx) {
|
||||
|
@ -406,14 +407,14 @@ function mergeSingletons(a, b, rootIsWildcard, mergeCache) {
|
|||
// merge parents x and y, giving array node with x,y then remainders
|
||||
// of those graphs. dup a, a' points at merged array
|
||||
// new joined parent so create new singleton pointing to it, a'
|
||||
var spc = SingletonPredictionContext.create(parent, a.returnState);
|
||||
const spc = SingletonPredictionContext.create(parent, a.returnState);
|
||||
if (mergeCache !== null) {
|
||||
mergeCache.set(a, b, spc);
|
||||
}
|
||||
return spc;
|
||||
} else { // a != b payloads differ
|
||||
// see if we can collapse parents due to $+x parents if local ctx
|
||||
var singleParent = null;
|
||||
let singleParent = null;
|
||||
if (a === b || (a.parentCtx !== null && a.parentCtx === b.parentCtx)) { // ax +
|
||||
// bx =
|
||||
// [a,b]x
|
||||
|
@ -421,13 +422,13 @@ function mergeSingletons(a, b, rootIsWildcard, mergeCache) {
|
|||
}
|
||||
if (singleParent !== null) { // parents are same
|
||||
// sort payloads and use same parent
|
||||
var payloads = [ a.returnState, b.returnState ];
|
||||
const payloads = [ a.returnState, b.returnState ];
|
||||
if (a.returnState > b.returnState) {
|
||||
payloads[0] = b.returnState;
|
||||
payloads[1] = a.returnState;
|
||||
}
|
||||
var parents = [ singleParent, singleParent ];
|
||||
var apc = new ArrayPredictionContext(parents, payloads);
|
||||
const parents = [ singleParent, singleParent ];
|
||||
const apc = new ArrayPredictionContext(parents, payloads);
|
||||
if (mergeCache !== null) {
|
||||
mergeCache.set(a, b, apc);
|
||||
}
|
||||
|
@ -436,14 +437,14 @@ function mergeSingletons(a, b, rootIsWildcard, mergeCache) {
|
|||
// parents differ and can't merge them. Just pack together
|
||||
// into array; can't merge.
|
||||
// ax + by = [ax,by]
|
||||
var payloads = [ a.returnState, b.returnState ];
|
||||
var parents = [ a.parentCtx, b.parentCtx ];
|
||||
const payloads = [ a.returnState, b.returnState ];
|
||||
let parents = [ a.parentCtx, b.parentCtx ];
|
||||
if (a.returnState > b.returnState) { // sort by payload
|
||||
payloads[0] = b.returnState;
|
||||
payloads[1] = a.returnState;
|
||||
parents = [ b.parentCtx, a.parentCtx ];
|
||||
}
|
||||
var a_ = new ArrayPredictionContext(parents, payloads);
|
||||
const a_ = new ArrayPredictionContext(parents, payloads);
|
||||
if (mergeCache !== null) {
|
||||
mergeCache.set(a, b, a_);
|
||||
}
|
||||
|
@ -451,44 +452,44 @@ function mergeSingletons(a, b, rootIsWildcard, mergeCache) {
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Handle case where at least one of {@code a} or {@code b} is
|
||||
// {@link //EMPTY}. In the following diagrams, the symbol {@code $} is used
|
||||
// to represent {@link //EMPTY}.
|
||||
//
|
||||
// <h2>Local-Context Merges</h2>
|
||||
//
|
||||
// <p>These local-context merge operations are used when {@code rootIsWildcard}
|
||||
// is true.</p>
|
||||
//
|
||||
// <p>{@link //EMPTY} is superset of any graph; return {@link //EMPTY}.<br>
|
||||
// <embed src="images/LocalMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>{@link //EMPTY} and anything is {@code //EMPTY}, so merged parent is
|
||||
// {@code //EMPTY}; return left graph.<br>
|
||||
// <embed src="images/LocalMerge_EmptyParent.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Special case of last merge if local context.<br>
|
||||
// <embed src="images/LocalMerge_DiffRoots.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// <h2>Full-Context Merges</h2>
|
||||
//
|
||||
// <p>These full-context merge operations are used when {@code rootIsWildcard}
|
||||
// is false.</p>
|
||||
//
|
||||
// <p><embed src="images/FullMerge_EmptyRoots.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Must keep all contexts; {@link //EMPTY} in array is a special value (and
|
||||
// null parent).<br>
|
||||
// <embed src="images/FullMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p><embed src="images/FullMerge_SameRoot.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// @param a the first {@link SingletonPredictionContext}
|
||||
// @param b the second {@link SingletonPredictionContext}
|
||||
// @param rootIsWildcard {@code true} if this is a local-context merge,
|
||||
// otherwise false to indicate a full-context merge
|
||||
// /
|
||||
/**
|
||||
* Handle case where at least one of {@code a} or {@code b} is
|
||||
* {@link //EMPTY}. In the following diagrams, the symbol {@code $} is used
|
||||
* to represent {@link //EMPTY}.
|
||||
*
|
||||
* <h2>Local-Context Merges</h2>
|
||||
*
|
||||
* <p>These local-context merge operations are used when {@code rootIsWildcard}
|
||||
* is true.</p>
|
||||
*
|
||||
* <p>{@link //EMPTY} is superset of any graph; return {@link //EMPTY}.<br>
|
||||
* <embed src="images/LocalMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>{@link //EMPTY} and anything is {@code //EMPTY}, so merged parent is
|
||||
* {@code //EMPTY}; return left graph.<br>
|
||||
* <embed src="images/LocalMerge_EmptyParent.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Special case of last merge if local context.<br>
|
||||
* <embed src="images/LocalMerge_DiffRoots.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* <h2>Full-Context Merges</h2>
|
||||
*
|
||||
* <p>These full-context merge operations are used when {@code rootIsWildcard}
|
||||
* is false.</p>
|
||||
*
|
||||
* <p><embed src="images/FullMerge_EmptyRoots.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Must keep all contexts; {@link //EMPTY} in array is a special value (and
|
||||
* null parent).<br>
|
||||
* <embed src="images/FullMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p><embed src="images/FullMerge_SameRoot.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* @param a the first {@link SingletonPredictionContext}
|
||||
* @param b the second {@link SingletonPredictionContext}
|
||||
* @param rootIsWildcard {@code true} if this is a local-context merge,
|
||||
* otherwise false to indicate a full-context merge
|
||||
*/
|
||||
function mergeRoot(a, b, rootIsWildcard) {
|
||||
if (rootIsWildcard) {
|
||||
if (a === PredictionContext.EMPTY) {
|
||||
|
@ -501,42 +502,42 @@ function mergeRoot(a, b, rootIsWildcard) {
|
|||
if (a === PredictionContext.EMPTY && b === PredictionContext.EMPTY) {
|
||||
return PredictionContext.EMPTY; // $ + $ = $
|
||||
} else if (a === PredictionContext.EMPTY) { // $ + x = [$,x]
|
||||
var payloads = [ b.returnState,
|
||||
const payloads = [ b.returnState,
|
||||
PredictionContext.EMPTY_RETURN_STATE ];
|
||||
var parents = [ b.parentCtx, null ];
|
||||
const parents = [ b.parentCtx, null ];
|
||||
return new ArrayPredictionContext(parents, payloads);
|
||||
} else if (b === PredictionContext.EMPTY) { // x + $ = [$,x] ($ is always first if present)
|
||||
var payloads = [ a.returnState, PredictionContext.EMPTY_RETURN_STATE ];
|
||||
var parents = [ a.parentCtx, null ];
|
||||
const payloads = [ a.returnState, PredictionContext.EMPTY_RETURN_STATE ];
|
||||
const parents = [ a.parentCtx, null ];
|
||||
return new ArrayPredictionContext(parents, payloads);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// Merge two {@link ArrayPredictionContext} instances.
|
||||
//
|
||||
// <p>Different tops, different parents.<br>
|
||||
// <embed src="images/ArrayMerge_DiffTopDiffPar.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Shared top, same parents.<br>
|
||||
// <embed src="images/ArrayMerge_ShareTopSamePar.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Shared top, different parents.<br>
|
||||
// <embed src="images/ArrayMerge_ShareTopDiffPar.svg" type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Shared top, all shared parents.<br>
|
||||
// <embed src="images/ArrayMerge_ShareTopSharePar.svg"
|
||||
// type="image/svg+xml"/></p>
|
||||
//
|
||||
// <p>Equal tops, merge parents and reduce top to
|
||||
// {@link SingletonPredictionContext}.<br>
|
||||
// <embed src="images/ArrayMerge_EqualTop.svg" type="image/svg+xml"/></p>
|
||||
// /
|
||||
/**
|
||||
* Merge two {@link ArrayPredictionContext} instances.
|
||||
*
|
||||
* <p>Different tops, different parents.<br>
|
||||
* <embed src="images/ArrayMerge_DiffTopDiffPar.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Shared top, same parents.<br>
|
||||
* <embed src="images/ArrayMerge_ShareTopSamePar.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Shared top, different parents.<br>
|
||||
* <embed src="images/ArrayMerge_ShareTopDiffPar.svg" type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Shared top, all shared parents.<br>
|
||||
* <embed src="images/ArrayMerge_ShareTopSharePar.svg"
|
||||
* type="image/svg+xml"/></p>
|
||||
*
|
||||
* <p>Equal tops, merge parents and reduce top to
|
||||
* {@link SingletonPredictionContext}.<br>
|
||||
* <embed src="images/ArrayMerge_EqualTop.svg" type="image/svg+xml"/></p>
|
||||
*/
|
||||
function mergeArrays(a, b, rootIsWildcard, mergeCache) {
|
||||
if (mergeCache !== null) {
|
||||
var previous = mergeCache.get(a, b);
|
||||
let previous = mergeCache.get(a, b);
|
||||
if (previous !== null) {
|
||||
return previous;
|
||||
}
|
||||
|
@ -546,31 +547,30 @@ function mergeArrays(a, b, rootIsWildcard, mergeCache) {
|
|||
}
|
||||
}
|
||||
// merge sorted payloads a + b => M
|
||||
var i = 0; // walks a
|
||||
var j = 0; // walks b
|
||||
var k = 0; // walks target M array
|
||||
let i = 0; // walks a
|
||||
let j = 0; // walks b
|
||||
let k = 0; // walks target M array
|
||||
|
||||
var mergedReturnStates = [];
|
||||
var mergedParents = [];
|
||||
let mergedReturnStates = [];
|
||||
let mergedParents = [];
|
||||
// walk and merge to yield mergedParents, mergedReturnStates
|
||||
while (i < a.returnStates.length && j < b.returnStates.length) {
|
||||
var a_parent = a.parents[i];
|
||||
var b_parent = b.parents[j];
|
||||
const a_parent = a.parents[i];
|
||||
const b_parent = b.parents[j];
|
||||
if (a.returnStates[i] === b.returnStates[j]) {
|
||||
// same payload (stack tops are equal), must yield merged singleton
|
||||
var payload = a.returnStates[i];
|
||||
const payload = a.returnStates[i];
|
||||
// $+$ = $
|
||||
var bothDollars = payload === PredictionContext.EMPTY_RETURN_STATE &&
|
||||
const bothDollars = payload === PredictionContext.EMPTY_RETURN_STATE &&
|
||||
a_parent === null && b_parent === null;
|
||||
var ax_ax = (a_parent !== null && b_parent !== null && a_parent === b_parent); // ax+ax
|
||||
const ax_ax = (a_parent !== null && b_parent !== null && a_parent === b_parent); // ax+ax
|
||||
// ->
|
||||
// ax
|
||||
if (bothDollars || ax_ax) {
|
||||
mergedParents[k] = a_parent; // choose left
|
||||
mergedReturnStates[k] = payload;
|
||||
} else { // ax+ay -> a'[x,y]
|
||||
var mergedParent = merge(a_parent, b_parent, rootIsWildcard, mergeCache);
|
||||
mergedParents[k] = mergedParent;
|
||||
mergedParents[k] = merge(a_parent, b_parent, rootIsWildcard, mergeCache);
|
||||
mergedReturnStates[k] = payload;
|
||||
}
|
||||
i += 1; // hop over left one as usual
|
||||
|
@ -588,13 +588,13 @@ function mergeArrays(a, b, rootIsWildcard, mergeCache) {
|
|||
}
|
||||
// copy over any payloads remaining in either array
|
||||
if (i < a.returnStates.length) {
|
||||
for (var p = i; p < a.returnStates.length; p++) {
|
||||
for (let p = i; p < a.returnStates.length; p++) {
|
||||
mergedParents[k] = a.parents[p];
|
||||
mergedReturnStates[k] = a.returnStates[p];
|
||||
k += 1;
|
||||
}
|
||||
} else {
|
||||
for (var p = j; p < b.returnStates.length; p++) {
|
||||
for (let p = j; p < b.returnStates.length; p++) {
|
||||
mergedParents[k] = b.parents[p];
|
||||
mergedReturnStates[k] = b.returnStates[p];
|
||||
k += 1;
|
||||
|
@ -603,7 +603,7 @@ function mergeArrays(a, b, rootIsWildcard, mergeCache) {
|
|||
// trim merged if we combined a few that had same stack tops
|
||||
if (k < mergedParents.length) { // write index < last position; trim
|
||||
if (k === 1) { // for just one merged element, return singleton top
|
||||
var a_ = SingletonPredictionContext.create(mergedParents[0],
|
||||
const a_ = SingletonPredictionContext.create(mergedParents[0],
|
||||
mergedReturnStates[0]);
|
||||
if (mergeCache !== null) {
|
||||
mergeCache.set(a, b, a_);
|
||||
|
@ -614,7 +614,7 @@ function mergeArrays(a, b, rootIsWildcard, mergeCache) {
|
|||
mergedReturnStates = mergedReturnStates.slice(0, k);
|
||||
}
|
||||
|
||||
var M = new ArrayPredictionContext(mergedParents, mergedReturnStates);
|
||||
const M = new ArrayPredictionContext(mergedParents, mergedReturnStates);
|
||||
|
||||
// if we created same array as a or b, return that instead
|
||||
// TODO: track whether this is possible above during merge sort for speed
|
||||
|
@ -638,20 +638,20 @@ function mergeArrays(a, b, rootIsWildcard, mergeCache) {
|
|||
return M;
|
||||
}
|
||||
|
||||
//
|
||||
// Make pass over all <em>M</em> {@code parents}; merge any {@code equals()}
|
||||
// ones.
|
||||
// /
|
||||
/**
|
||||
* Make pass over all <em>M</em> {@code parents}; merge any {@code equals()}
|
||||
* ones.
|
||||
*/
|
||||
function combineCommonParents(parents) {
|
||||
var uniqueParents = new Map();
|
||||
const uniqueParents = new Map();
|
||||
|
||||
for (var p = 0; p < parents.length; p++) {
|
||||
var parent = parents[p];
|
||||
for (let p = 0; p < parents.length; p++) {
|
||||
const parent = parents[p];
|
||||
if (!(uniqueParents.containsKey(parent))) {
|
||||
uniqueParents.put(parent, parent);
|
||||
}
|
||||
}
|
||||
for (var q = 0; q < parents.length; q++) {
|
||||
for (let q = 0; q < parents.length; q++) {
|
||||
parents[q] = uniqueParents.get(parents[q]);
|
||||
}
|
||||
}
|
||||
|
@ -660,7 +660,7 @@ function getCachedPredictionContext(context, contextCache, visited) {
|
|||
if (context.isEmpty()) {
|
||||
return context;
|
||||
}
|
||||
var existing = visited.get(context) || null;
|
||||
let existing = visited.get(context) || null;
|
||||
if (existing !== null) {
|
||||
return existing;
|
||||
}
|
||||
|
@ -669,14 +669,14 @@ function getCachedPredictionContext(context, contextCache, visited) {
|
|||
visited.put(context, existing);
|
||||
return existing;
|
||||
}
|
||||
var changed = false;
|
||||
var parents = [];
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
var parent = getCachedPredictionContext(context.getParent(i), contextCache, visited);
|
||||
let changed = false;
|
||||
let parents = [];
|
||||
for (let i = 0; i < parents.length; i++) {
|
||||
const parent = getCachedPredictionContext(context.getParent(i), contextCache, visited);
|
||||
if (changed || parent !== context.getParent(i)) {
|
||||
if (!changed) {
|
||||
parents = [];
|
||||
for (var j = 0; j < context.length; j++) {
|
||||
for (let j = 0; j < context.length; j++) {
|
||||
parents[j] = context.getParent(j);
|
||||
}
|
||||
changed = true;
|
||||
|
@ -689,7 +689,7 @@ function getCachedPredictionContext(context, contextCache, visited) {
|
|||
visited.put(context, context);
|
||||
return context;
|
||||
}
|
||||
var updated = null;
|
||||
let updated = null;
|
||||
if (parents.length === 0) {
|
||||
updated = PredictionContext.EMPTY;
|
||||
} else if (parents.length === 1) {
|
||||
|
@ -719,16 +719,18 @@ function getAllContextNodes(context, nodes, visited) {
|
|||
}
|
||||
visited.put(context, context);
|
||||
nodes.push(context);
|
||||
for (var i = 0; i < context.length; i++) {
|
||||
for (let i = 0; i < context.length; i++) {
|
||||
getAllContextNodes(context.getParent(i), nodes, visited);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
||||
exports.merge = merge;
|
||||
exports.PredictionContext = PredictionContext;
|
||||
exports.PredictionContextCache = PredictionContextCache;
|
||||
exports.SingletonPredictionContext = SingletonPredictionContext;
|
||||
exports.predictionContextFromRuleContext = predictionContextFromRuleContext;
|
||||
exports.getCachedPredictionContext = getCachedPredictionContext;
|
||||
module.exports = {
|
||||
merge,
|
||||
PredictionContext,
|
||||
PredictionContextCache,
|
||||
SingletonPredictionContext,
|
||||
predictionContextFromRuleContext,
|
||||
getCachedPredictionContext
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue