fix various hashing issues

This commit is contained in:
Eric Vergnaud 2016-12-04 14:23:30 +08:00
parent e850b4d97f
commit 7332c1eb02
6 changed files with 156 additions and 48 deletions

View File

@ -35,6 +35,7 @@
var Token = require('./Token').Token;
var Recognizer = require('./Recognizer').Recognizer;
var CommonTokenFactory = require('./CommonTokenFactory').CommonTokenFactory;
var RecognitionException = require('./error/Errors').RecognitionException;
var LexerNoViableAltException = require('./error/Errors').LexerNoViableAltException;
function TokenSource() {
@ -150,9 +151,13 @@ Lexer.prototype.nextToken = function() {
try {
ttype = this._interp.match(this._input, this._mode);
} catch (e) {
console.log(e.stack);
this.notifyListeners(e); // report error
this.recover(e);
if(e instanceof RecognitionException) {
this.notifyListeners(e); // report error
this.recover(e);
} else {
console.log(e.stack);
throw e;
}
}
if (this._input.LA(1) === Token.EOF) {
this._hitEOF = true;

View File

@ -64,13 +64,13 @@ function standardEqualsFunction(a, b) {
return a.equals(b);
}
function standardHashFunction(a) {
function standardHashCodeFunction(a) {
return a.hashCode();
}
function Set(hashFunction, equalsFunction) {
this.data = {};
this.hashFunction = hashFunction || standardHashFunction;
this.hashFunction = hashFunction || standardHashCodeFunction;
this.equalsFunction = equalsFunction || standardEqualsFunction;
return this;
}
@ -91,9 +91,8 @@ Set.prototype.add = function (value) {
var hash = this.hashFunction(value);
var key = "hash_" + hash;
if (key in this.data) {
var i;
var values = this.data[key];
for (i = 0; i < values.length; i++) {
for (var i = 0; i < values.length; i++) {
if (this.equalsFunction(value, values[i])) {
return values[i];
}
@ -114,9 +113,8 @@ Set.prototype.get = function (value) {
var hash = this.hashFunction(value);
var key = "hash_" + hash;
if (key in this.data) {
var i;
var values = this.data[key];
for (i = 0; i < values.length; i++) {
for (var i = 0; i < values.length; i++) {
if (this.equalsFunction(value, values[i])) {
return values[i];
}
@ -194,11 +192,110 @@ BitSet.prototype.toString = function () {
return "{" + this.values().join(", ") + "}";
};
function Map(hashFunction, equalsFunction) {
this.data = {};
this.hashFunction = hashFunction || standardHashCodeFunction;
this.equalsFunction = equalsFunction || standardEqualsFunction;
return this;
}
Object.defineProperty(Map.prototype, "length", {
get: function () {
var l = 0;
for (var hashKey in this.data) {
if (hashKey.indexOf("hash_") === 0) {
l = l + this.data[hashKey].length;
}
}
return l;
}
});
Map.prototype.put = function (key, value) {
var hashKey = "hash_" + this.hashFunction(key);
if (hashKey in this.data) {
var entries = this.data[hashKey];
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
if (this.equalsFunction(key, entry.key)) {
var oldValue = entry.value;
entry.value = value;
return oldValue;
}
}
entries.push({key:key, value:value});
return value;
} else {
this.data[hashKey] = [{key:key, value:value}];
return value;
}
};
Map.prototype.containsKey = function (key) {
var hashKey = "hash_" + this.hashFunction(key);
if(hashKey in this.data) {
var entries = this.data[hashKey];
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
if (this.equalsFunction(key, entry.key))
return true;
}
}
return false;
};
Map.prototype.get = function (key) {
var hashKey = "hash_" + this.hashFunction(key);
if(hashKey in this.data) {
var entries = this.data[hashKey];
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
if (this.equalsFunction(key, entry.key))
return entry.value;
}
}
return null;
};
Map.prototype.entries = function () {
var l = [];
for (var key in this.data) {
if (key.indexOf("hash_") === 0) {
l = l.concat(this.data[key]);
}
}
return l;
};
Map.prototype.getKeys = function () {
return this.entries().map(function(e) {
return e.key;
});
};
Map.prototype.getValues = function () {
return this.entries().map(function(e) {
return e.value;
});
};
Map.prototype.toString = function () {
var ss = this.entries().map(function(entry) {
return '{' + entry.key + ':' + entry.value + '}';
});
return '[' + ss.join(", ") + ']';
};
function AltDict() {
this.data = {};
return this;
}
AltDict.prototype.get = function (key) {
key = "k-" + key;
if (key in this.data) {
@ -248,8 +345,12 @@ Hash.prototype.update = function () {
case 'boolean':
k = value;
break;
default:
case 'string':
k = value.hashCode();
break;
default:
value.updateHashCode(this);
continue;
}
k = k * 0xCC9E2D51;
k = (k << 15) | (k >>> (32 - 15));
@ -273,7 +374,7 @@ Hash.prototype.finish = function () {
return hash;
}
exports.hashStuff = function() {
function hashStuff() {
var hash = new Hash();
hash.update.apply(arguments);
return hash.finish();
@ -304,13 +405,13 @@ function escapeWhitespace(s, escapeSpaces) {
return s;
}
exports.titleCase = function (str) {
function titleCase(str) {
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1);
});
};
exports.equalArrays = function(a, b)
function equalArrays(a, b)
{
if (!Array.isArray(a) || !Array.isArray(b))
return false;
@ -327,10 +428,14 @@ exports.equalArrays = function(a, b)
return true;
};
exports.Hash = Hash;
exports.Set = Set;
exports.Map = Map;
exports.BitSet = BitSet;
exports.AltDict = AltDict;
exports.DoubleDict = DoubleDict;
exports.hashStuff = hashStuff;
exports.escapeWhitespace = escapeWhitespace;
exports.arrayToString = arrayToString;
exports.Hash = Hash;
exports.titleCase = titleCase;
exports.equalArrays = equalArrays;

View File

@ -198,21 +198,25 @@ ATNConfigSet.prototype.equals = function(other) {
};
ATNConfigSet.prototype.hashCode = function() {
var hash = new Hash();
this.updateHashCode(hash);
return hash.finish();
};
ATNConfigSet.prototype.updateHashCode = function(hash) {
if (this.readOnly) {
if (this.cachedHashCode === -1) {
this.cachedHashCode = this.hashConfigs();
var hash = new Hash();
hash.update(this.configs);
this.cachedHashCode = hash.finish();
}
return this.cachedHashCode;
hash.update(this.cachedHashCode);
} else {
return this.hashConfigs();
hash.update(this.configs);
}
};
ATNConfigSet.prototype.hashConfigs = function() {
var hash = new Hash();
hash.update(this.configs);
return hash.finish();
};
Object.defineProperty(ATNConfigSet.prototype, "length", {
get : function() {

View File

@ -162,6 +162,11 @@ LexerActionExecutor.prototype.hashCode = function() {
return this.cachedHashCode;
};
LexerActionExecutor.prototype.updateHashCode = function(hash) {
hash.update(this.cachedHashCode);
};
LexerActionExecutor.prototype.equals = function(other) {
if (this === other) {
return true;

View File

@ -1703,8 +1703,7 @@ ParserATNSimulator.prototype.addDFAState = function(dfa, D) {
if (D == ATNSimulator.ERROR) {
return D;
}
var hash = "key_" + D.hashCode();
var existing = dfa.states[hash] || null;
var existing = dfa.states.get(D);
if(existing!==null) {
return existing;
}
@ -1713,7 +1712,7 @@ ParserATNSimulator.prototype.addDFAState = function(dfa, D) {
D.configs.optimizeConfigs(this);
D.configs.setReadonly(true);
}
dfa.states[hash] = D;
dfa.states.add(D);
if (this.debug) {
console.log("adding new DFA state: " + D);
}

View File

@ -34,6 +34,7 @@
// ambiguities.
var Set = require('./../Utils').Set;
var Map = require('./../Utils').Map;
var BitSet = require('./../Utils').BitSet;
var AltDict = require('./../Utils').AltDict;
var ATN = require('./ATN').ATN;
@ -42,6 +43,8 @@ var ATNConfigSet = require('./ATNConfigSet').ATNConfigSet;
var ATNConfig = require('./ATNConfig').ATNConfig;
var SemanticContext = require('./SemanticContext').SemanticContext;
var Hash = require("../Utils").Hash;
var hashStuff = require('./../Utils').hashStuff;
var equalArrays = require('./../Utils').equalArrays;
function PredictionMode() {
return this;
@ -516,33 +519,20 @@ PredictionMode.getAlts = function(altsets) {
// map[c] U= c.{@link ATNConfig//alt alt} // map hash/equals uses s and x, not
// alt and not pred
// </pre>
//
PredictionMode.getConflictingAltSubsets = function(configs) {
var configToAlts = {};
for(var i=0;i<configs.items.length;i++) {
var c = configs.items[i];
var hash = new Hash();
hash.update(c.state.stateNumber);
if(!c.context.updateHashCode)
c;
c.context.updateHashCode(hash);
var hashed = hash.finish()
var key = "key_" + hashed;
var alts = configToAlts[key] || null;
var configToAlts = new Map();
configToAlts.hashFunction = function(cfg) { hashStuff(cfg.state.stateNumber, cfg.context); };
configToAlts.equalsFunction = function(c1, c2) { return c1.state.stateNumber==c2.state.stateNumber && c1.context.equals(c2.context);}
configs.items.map(function(cfg) {
var alts = configToAlts.get(cfg);
if (alts === null) {
alts = new BitSet();
configToAlts[key] = alts;
configToAlts.put(cfg, alts);
}
alts.add(c.alt);
}
var values = [];
for(var k in configToAlts) {
if(k.indexOf("key_")!==0) {
continue;
}
values.push(configToAlts[k]);
}
return values;
alts.add(cfg.alt);
});
return configToAlts.getValues();
};
//