fullgen.js | |
---|---|
var xjst = require('../../xjst'),
utils = xjst.utils; | |
function Merger ()Merge common tree parts and identify them | function Merger(identifier) {
this.cache = {};
this.identifier = identifier || new utils.Identifier();
} |
function merge (obj)@obj {Object} part of ASTSearches cache for obj and for all it's successors If found - returns cached object instead of original If not - pushes object to cache and returns it | Merger.prototype.merge = function merge(obj) {
var self = this,
hash,
size;
if (obj.tag) {
hash = ['switch ', JSON.stringify(obj.tag), '{'];
size = 0;
obj.cases.forEach(function (c){
c[1] = self.merge(c[1]);
size += c[1].size;
hash.push(c[1].hash, ' ');
});
obj['default'] = self.merge(obj['default']);
size += obj['default'].size;
hash.push(obj['default'].hash, '}');
hash = utils.sha1(hash.join(''));
} else {
var json = JSON.stringify(obj.stmt);
hash = obj.hash || utils.sha1(json); |
XXX Strange Euristics | size = json.length / 3200;
}
obj.size = size;
if(this.cache[hash] !== undefined) {
obj.id = this.cache[hash].id;
} else {
this.cache[obj.hash = hash] = obj;
obj.id = this.identifier.generate();
}
return this.cache[hash];
}; |
function engine (templates, options, values)@templates {Array} AST@options {Object} Compiler options@config {Object} Engine configurationReturns optimized tree (via fullgen algorithm) | exports.execute = function engine(templates, options, config) {
var initialState = config.state,
values = config.values, |
Use initial id from config | merger = new Merger(config.id),
merge = merger.merge.bind(merger),
unique = new utils.Identifier();
function addNode(node, state) {
if (node.state) {
node.state.merge(state);
} else {
node.state = state.clone();
}
if (options.merge && node.tag) node.longId = node.state.hash();
return node;
}
function traverse(i, j, state) {
var template = templates[i]; |
If we stepped out of templates - we're in unexpected place throw exception | if (!template) {
return addNode(
merge({
unexpected: true,
fn: true,
stmt: ['throw', ['new', 'Error']]
}),
state
);
} |
If we stepped out of predicates - add template's body to tree | var subMatch = template[0][j];
if (!subMatch) {
return addNode(merge({ stmt: template[1], template: i }), state);
} |
Skip unreachable templates template (p1 === c1 && p2 === c2) | var known = template[0].slice(j + 1).some(function(s) {
var predicate = utils.stringify(s[1]),
predicateConst = s[2];
return state.has(predicate) &&
!state.has(predicate, utils.stringify(predicateConst));
});
if (known) return traverse(i + 1, 0, state);
var predicate = utils.stringify(subMatch[1]),
predicateConst = subMatch[2]; |
If we already know value of this predicate | if (state.has(predicate)) { |
And if current template's predicate value equals to known | if (state.has(predicate, utils.stringify(predicateConst))) { |
Skip this predicate and go further | return traverse(i, j + 1, state);
} else { |
Skip whole template as it's unreachable | return traverse(i + 1, 0, state);
}
} else { |
Create switch for current predicate and all known values of it | var result = {};
result.tag = subMatch[1];
result.tagId = subMatch[0];
result.tagStr = predicate;
result.cases = values[predicate].map(function(v) {
return [v, traverse(i, j,
xjst.state.create(state, predicate,
utils.stringify(v)))];
});
result['default'] = traverse(i, j, xjst.state.create(state,
predicate,
-unique.generate()));
return addNode(merge(result), state);
}
}
return traverse(0, 0, initialState.clone());
}; |
Export engine's name | exports.name = 'fullgen';
|