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

View File

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

View File

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

View File

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

View File

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