aho-corasick
Version:
Aho–Corasick string matching algorithm
187 lines (167 loc) • 4.89 kB
JavaScript
// Generated by CoffeeScript 1.4.0
(function() {
"use strict";
var AhoCorasick, Trie;
Trie = (function() {
function Trie() {
this.next = {};
this.is_word = null;
this.value = null;
this.data = [];
}
Trie.prototype.add = function(word, data, original_word) {
var chr, node;
chr = word.charAt(0);
node = this.next[chr];
if (!node) {
node = this.next[chr] = new Trie();
if (original_word) {
node.value = original_word.substr(0, original_word.length - word.length + 1);
} else {
node.value = chr;
}
}
if (word.length > 1) {
return node.add(word.substring(1), data, original_word || word);
} else {
node.data.push(data);
return node.is_word = true;
}
};
Trie.prototype.explore_fail_link = function(word) {
var chr, i, node, _i, _ref;
node = this;
for (i = _i = 0, _ref = word.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
chr = word.charAt(i);
node = node.next[chr];
if (!node) {
return null;
}
}
return node;
};
Trie.prototype.each_node = function(callback) {
var node, _k, _ref, _ref1;
_ref = this.next;
for (_k in _ref) {
node = _ref[_k];
callback(this, node);
}
_ref1 = this.next;
for (_k in _ref1) {
node = _ref1[_k];
node.each_node(callback);
}
return this;
};
return Trie;
})();
AhoCorasick = (function() {
function AhoCorasick() {
this.trie = new Trie();
}
AhoCorasick.prototype.add = function(word, data) {
return this.trie.add(word, data);
};
AhoCorasick.prototype.build_fail = function(node) {
var fail_node, i, sub_node, _i, _k, _ref, _ref1;
node = node || this.trie;
node.fail = null;
if (node.value) {
for (i = _i = 1, _ref = node.value.length; 1 <= _ref ? _i < _ref : _i > _ref; i = 1 <= _ref ? ++_i : --_i) {
fail_node = this.trie.explore_fail_link(node.value.substring(i));
if (fail_node) {
node.fail = fail_node;
break;
}
}
}
_ref1 = node.next;
for (_k in _ref1) {
sub_node = _ref1[_k];
this.build_fail(sub_node);
}
return this;
};
AhoCorasick.prototype.foreach_match = function(node, pos, callback) {
var offset;
while (node) {
if (node.is_word) {
offset = pos - node.value.length;
callback(node.value, node.data, offset);
}
node = node.fail;
}
return this;
};
AhoCorasick.prototype.search = function(string, callback) {
var chr, current, idx, _i, _ref;
current = this.trie;
for (idx = _i = 0, _ref = string.length; 0 <= _ref ? _i < _ref : _i > _ref; idx = 0 <= _ref ? ++_i : --_i) {
chr = string.charAt(idx);
while (current && !current.next[chr]) {
current = current.fail;
}
if (!current) {
current = this.trie;
}
if (current.next[chr]) {
current = current.next[chr];
if (callback) {
this.foreach_match(current, idx + 1, callback);
}
}
}
return this;
};
AhoCorasick.prototype.to_dot = function() {
var dot, fail_cb, last_chr, link_cb, v_;
dot = ['digraph Trie {'];
v_ = function(node) {
if (node && node.value) {
return "\"" + node.value + "\"";
} else {
return "\"\"";
}
};
last_chr = function(str) {
if (str) {
return str.charAt(str.length - 1);
}
};
link_cb = function(from, to) {
var k, option, to_label, to_opt, v;
to_label = last_chr(to.value);
to_opt = ["label = \"" + to_label + "\""];
if (to.is_word) {
option = {
style: 'filled',
color: 'skyblue'
};
for (k in option) {
v = option[k];
to_opt.push("" + k + " = \"" + v + "\"");
}
}
dot.push("" + (v_(from)) + " -> " + (v_(to)) + ";");
dot.push("" + (v_(to)) + " [ " + (to_opt.join(',')) + " ];");
return fail_cb(from, to);
};
fail_cb = function(from, to) {
var style, _ref;
_ref = [to, to.fail], from = _ref[0], to = _ref[1];
style = to ? 'dashed' : 'dotted';
return dot.push("" + (v_(from)) + " -> " + (v_(to)) + " [ style = \"" + style + "\" ];");
};
this.trie.each_node(link_cb);
dot.push('}');
return dot.join("\n");
};
return AhoCorasick;
})();
if (module) {
module.exports = AhoCorasick;
} else {
window.AhoCorasick = AhoCorasick;
}
}).call(this);