|
|
@ -19,22 +19,25 @@ function Ruler() { |
|
|
|
// alt: [ name2, name3 ]
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
this.rules = []; |
|
|
|
this.__rules__ = []; |
|
|
|
|
|
|
|
// Cached rule chains.
|
|
|
|
//
|
|
|
|
// First level - chain name, '' for default.
|
|
|
|
// Second level - diginal anchor for fast filtering by charcodes.
|
|
|
|
//
|
|
|
|
this.cache = null; |
|
|
|
this.__cache__ = null; |
|
|
|
} |
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Helper methods, should not be used directly
|
|
|
|
|
|
|
|
|
|
|
|
// Find rule index by name
|
|
|
|
//
|
|
|
|
Ruler.prototype.find = function (name) { |
|
|
|
for (var i = 0; i < this.rules.length; i++) { |
|
|
|
if (this.rules[i].name === name) { |
|
|
|
Ruler.prototype.__find__ = function (name) { |
|
|
|
for (var i = 0; i < this.__rules__.length; i++) { |
|
|
|
if (this.__rules__[i].name === name) { |
|
|
|
return i; |
|
|
|
} |
|
|
|
} |
|
|
@ -42,55 +45,91 @@ Ruler.prototype.find = function (name) { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Build rules lookup cache
|
|
|
|
//
|
|
|
|
Ruler.prototype.__compile__ = function () { |
|
|
|
var self = this; |
|
|
|
var chains = [ '' ]; |
|
|
|
|
|
|
|
// collect unique names
|
|
|
|
self.__rules__.forEach(function (rule) { |
|
|
|
if (!rule.enabled) { return; } |
|
|
|
|
|
|
|
rule.alt.forEach(function (altName) { |
|
|
|
if (chains.indexOf(altName) < 0) { |
|
|
|
chains.push(altName); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
self.__cache__ = {}; |
|
|
|
|
|
|
|
chains.forEach(function (chain) { |
|
|
|
self.__cache__[chain] = []; |
|
|
|
self.__rules__.forEach(function (rule) { |
|
|
|
if (!rule.enabled) { return; } |
|
|
|
|
|
|
|
if (chain && rule.alt.indexOf(chain) < 0) { return; } |
|
|
|
|
|
|
|
self.__cache__[chain].push(rule.fn); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Public methods
|
|
|
|
|
|
|
|
|
|
|
|
// Replace rule function
|
|
|
|
//
|
|
|
|
Ruler.prototype.at = function (name, fn, options) { |
|
|
|
var index = this.find(name); |
|
|
|
var index = this.__find__(name); |
|
|
|
var opt = options || {}; |
|
|
|
|
|
|
|
if (index === -1) { throw new Error('Parser rule not found: ' + name); } |
|
|
|
|
|
|
|
this.rules[index].fn = fn; |
|
|
|
this.rules[index].alt = opt.alt || []; |
|
|
|
this.cache = null; |
|
|
|
this.__rules__[index].fn = fn; |
|
|
|
this.__rules__[index].alt = opt.alt || []; |
|
|
|
this.__cache__ = null; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Add rule to chain before one with given name.
|
|
|
|
//
|
|
|
|
Ruler.prototype.before = function (beforeName, ruleName, fn, options) { |
|
|
|
var index = this.find(beforeName); |
|
|
|
var index = this.__find__(beforeName); |
|
|
|
var opt = options || {}; |
|
|
|
|
|
|
|
if (index === -1) { throw new Error('Parser rule not found: ' + beforeName); } |
|
|
|
|
|
|
|
this.rules.splice(index, 0, { |
|
|
|
this.__rules__.splice(index, 0, { |
|
|
|
name: ruleName, |
|
|
|
enabled: true, |
|
|
|
fn: fn, |
|
|
|
alt: opt.alt || [] |
|
|
|
}); |
|
|
|
|
|
|
|
this.cache = null; |
|
|
|
this.__cache__ = null; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Add rule to chain after one with given name.
|
|
|
|
//
|
|
|
|
Ruler.prototype.after = function (afterName, ruleName, fn, options) { |
|
|
|
var index = this.find(afterName); |
|
|
|
var index = this.__find__(afterName); |
|
|
|
var opt = options || {}; |
|
|
|
|
|
|
|
if (index === -1) { throw new Error('Parser rule not found: ' + afterName); } |
|
|
|
|
|
|
|
this.rules.splice(index + 1, 0, { |
|
|
|
this.__rules__.splice(index + 1, 0, { |
|
|
|
name: ruleName, |
|
|
|
enabled: true, |
|
|
|
fn: fn, |
|
|
|
alt: opt.alt || [] |
|
|
|
}); |
|
|
|
|
|
|
|
this.cache = null; |
|
|
|
this.__cache__ = null; |
|
|
|
}; |
|
|
|
|
|
|
|
// Add rule to the end of chain.
|
|
|
@ -98,14 +137,14 @@ Ruler.prototype.after = function (afterName, ruleName, fn, options) { |
|
|
|
Ruler.prototype.push = function (ruleName, fn, options) { |
|
|
|
var opt = options || {}; |
|
|
|
|
|
|
|
this.rules.push({ |
|
|
|
this.__rules__.push({ |
|
|
|
name: ruleName, |
|
|
|
enabled: true, |
|
|
|
fn: fn, |
|
|
|
alt: opt.alt || [] |
|
|
|
}); |
|
|
|
|
|
|
|
this.cache = null; |
|
|
|
this.__cache__ = null; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -119,21 +158,21 @@ Ruler.prototype.enable = function (list, strict) { |
|
|
|
|
|
|
|
// In strict mode disable all existing rules first
|
|
|
|
if (strict) { |
|
|
|
this.rules.forEach(function (rule) { |
|
|
|
this.__rules__.forEach(function (rule) { |
|
|
|
rule.enabled = false; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// Search by name and enable
|
|
|
|
list.forEach(function (name) { |
|
|
|
var idx = this.find(name); |
|
|
|
var idx = this.__find__(name); |
|
|
|
|
|
|
|
if (idx < 0) { throw new Error('Rules manager: invalid rule name ' + name); } |
|
|
|
this.rules[idx].enabled = true; |
|
|
|
this.__rules__[idx].enabled = true; |
|
|
|
|
|
|
|
}, this); |
|
|
|
|
|
|
|
this.cache = null; |
|
|
|
this.__cache__ = null; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -146,57 +185,25 @@ Ruler.prototype.disable = function (list) { |
|
|
|
|
|
|
|
// Search by name and disable
|
|
|
|
list.forEach(function (name) { |
|
|
|
var idx = this.find(name); |
|
|
|
var idx = this.__find__(name); |
|
|
|
|
|
|
|
if (idx < 0) { throw new Error('Rules manager: invalid rule name ' + name); } |
|
|
|
this.rules[idx].enabled = false; |
|
|
|
this.__rules__[idx].enabled = false; |
|
|
|
|
|
|
|
}, this); |
|
|
|
|
|
|
|
this.cache = null; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Build rules lookup cache
|
|
|
|
//
|
|
|
|
Ruler.prototype.compile = function () { |
|
|
|
var self = this; |
|
|
|
var chains = [ '' ]; |
|
|
|
|
|
|
|
// collect unique names
|
|
|
|
self.rules.forEach(function (rule) { |
|
|
|
if (!rule.enabled) { return; } |
|
|
|
|
|
|
|
rule.alt.forEach(function (altName) { |
|
|
|
if (chains.indexOf(altName) < 0) { |
|
|
|
chains.push(altName); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
self.cache = {}; |
|
|
|
|
|
|
|
chains.forEach(function (chain) { |
|
|
|
self.cache[chain] = []; |
|
|
|
self.rules.forEach(function (rule) { |
|
|
|
if (!rule.enabled) { return; } |
|
|
|
|
|
|
|
if (chain && rule.alt.indexOf(chain) < 0) { return; } |
|
|
|
|
|
|
|
self.cache[chain].push(rule.fn); |
|
|
|
}); |
|
|
|
}); |
|
|
|
this.__cache__ = null; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Get rules list as array of functions.
|
|
|
|
//
|
|
|
|
Ruler.prototype.getRules = function (chainName) { |
|
|
|
if (this.cache === null) { |
|
|
|
this.compile(); |
|
|
|
if (this.__cache__ === null) { |
|
|
|
this.__compile__(); |
|
|
|
} |
|
|
|
|
|
|
|
return this.cache[chainName]; |
|
|
|
return this.__cache__[chainName]; |
|
|
|
}; |
|
|
|
|
|
|
|
module.exports = Ruler; |
|
|
|