This repository has been archived on 2018-10-12. You can view files and clone it, but cannot push or open issues or pull requests.
node-task/node_modules/dustjs-linkedin/lib/compiler.js

419 lines
11 KiB
JavaScript
Raw Normal View History

2014-09-18 16:17:29 -04:00
/*jshint latedef:false */
(function(root, factory) {
if (typeof exports === 'object') {
// in Node, require this file if we want to use the compiler as a standalone module
module.exports = factory(require('./parser').parse, require('./dust'));
} else {
// in the browser, store the factory output if we want to use the compiler directly
factory(root.dust.parse, root.dust);
}
}(this, function(parse, dust) {
var compiler = {},
isArray = dust.isArray;
compiler.compile = function(source, name) {
// the name parameter is optional.
// this can happen for templates that are rendered immediately (renderSource which calls compileFn) or
// for templates that are compiled as a callable (compileFn)
//
// for the common case (using compile and render) a name is required so that templates will be cached by name and rendered later, by name.
if (!name && name !== null) {
throw new Error('Template name parameter cannot be undefined when calling dust.compile');
}
try {
var ast = filterAST(parse(source));
return compile(ast, name);
}
catch (err)
{
if (!err.line || !err.column) {
throw err;
}
throw new SyntaxError(err.message + ' At line : ' + err.line + ', column : ' + err.column);
}
};
function filterAST(ast) {
var context = {};
return compiler.filterNode(context, ast);
}
compiler.filterNode = function(context, node) {
return compiler.optimizers[node[0]](context, node);
};
compiler.optimizers = {
body: compactBuffers,
buffer: noop,
special: convertSpecial,
format: nullify, // TODO: convert format
reference: visit,
'#': visit,
'?': visit,
'^': visit,
'<': visit,
'+': visit,
'@': visit,
'%': visit,
partial: visit,
context: visit,
params: visit,
bodies: visit,
param: visit,
filters: noop,
key: noop,
path: noop,
literal: noop,
raw: noop,
comment: nullify,
line: nullify,
col: nullify
};
compiler.pragmas = {
esc: function(compiler, context, bodies, params) {
var old = compiler.auto,
out;
if (!context) {
context = 'h';
}
compiler.auto = (context === 's') ? '' : context;
out = compileParts(compiler, bodies.block);
compiler.auto = old;
return out;
}
};
function visit(context, node) {
var out = [node[0]],
i, len, res;
for (i=1, len=node.length; i<len; i++) {
res = compiler.filterNode(context, node[i]);
if (res) {
out.push(res);
}
}
return out;
}
// Compacts consecutive buffer nodes into a single node
function compactBuffers(context, node) {
var out = [node[0]],
memo, i, len, res;
for (i=1, len=node.length; i<len; i++) {
res = compiler.filterNode(context, node[i]);
if (res) {
if (res[0] === 'buffer') {
if (memo) {
memo[1] += res[1];
} else {
memo = res;
out.push(res);
}
} else {
memo = null;
out.push(res);
}
}
}
return out;
}
var specialChars = {
's': ' ',
'n': '\n',
'r': '\r',
'lb': '{',
'rb': '}'
};
function convertSpecial(context, node) {
return ['buffer', specialChars[node[1]]];
}
function noop(context, node) {
return node;
}
function nullify(){}
function compile(ast, name) {
var context = {
name: name,
bodies: [],
blocks: {},
index: 0,
auto: 'h'
};
return '(function(){dust.register(' +
(name ? '"' + name + '"' : 'null') + ',' +
compiler.compileNode(context, ast) +
');' +
compileBlocks(context) +
compileBodies(context) +
'return body_0;' +
'})();';
}
function compileBlocks(context) {
var out = [],
blocks = context.blocks,
name;
for (name in blocks) {
out.push('"' + name + '":' + blocks[name]);
}
if (out.length) {
context.blocks = 'ctx=ctx.shiftBlocks(blocks);';
return 'var blocks={' + out.join(',') + '};';
}
return context.blocks = '';
}
function compileBodies(context) {
var out = [],
bodies = context.bodies,
blx = context.blocks,
i, len;
for (i=0, len=bodies.length; i<len; i++) {
out[i] = 'function body_' + i + '(chk,ctx){' +
blx + 'return chk' + bodies[i] + ';}';
}
return out.join('');
}
function compileParts(context, body) {
var parts = '',
i, len;
for (i=1, len=body.length; i<len; i++) {
parts += compiler.compileNode(context, body[i]);
}
return parts;
}
compiler.compileNode = function(context, node) {
return compiler.nodes[node[0]](context, node);
};
compiler.nodes = {
body: function(context, node) {
var id = context.index++,
name = 'body_' + id;
context.bodies[id] = compileParts(context, node);
return name;
},
buffer: function(context, node) {
return '.write(' + escape(node[1]) + ')';
},
format: function(context, node) {
return '.write(' + escape(node[1] + node[2]) + ')';
},
reference: function(context, node) {
return '.reference(' + compiler.compileNode(context, node[1]) +
',ctx,' + compiler.compileNode(context, node[2]) + ')';
},
'#': function(context, node) {
return compileSection(context, node, 'section');
},
'?': function(context, node) {
return compileSection(context, node, 'exists');
},
'^': function(context, node) {
return compileSection(context, node, 'notexists');
},
'<': function(context, node) {
var bodies = node[4];
for (var i=1, len=bodies.length; i<len; i++) {
var param = bodies[i],
type = param[1][1];
if (type === 'block') {
context.blocks[node[1].text] = compiler.compileNode(context, param[2]);
return '';
}
}
return '';
},
'+': function(context, node) {
if (typeof(node[1].text) === 'undefined' && typeof(node[4]) === 'undefined'){
return '.block(ctx.getBlock(' +
compiler.compileNode(context, node[1]) +
',chk, ctx),' + compiler.compileNode(context, node[2]) + ', {},' +
compiler.compileNode(context, node[3]) +
')';
} else {
return '.block(ctx.getBlock(' +
escape(node[1].text) +
'),' + compiler.compileNode(context, node[2]) + ',' +
compiler.compileNode(context, node[4]) + ',' +
compiler.compileNode(context, node[3]) +
')';
}
},
'@': function(context, node) {
return '.helper(' +
escape(node[1].text) +
',' + compiler.compileNode(context, node[2]) + ',' +
compiler.compileNode(context, node[4]) + ',' +
compiler.compileNode(context, node[3]) +
')';
},
'%': function(context, node) {
// TODO: Move these hacks into pragma precompiler
var name = node[1][1],
rawBodies,
bodies,
rawParams,
params,
ctx, b, p, i, len;
if (!compiler.pragmas[name]) {
return '';
}
rawBodies = node[4];
bodies = {};
for (i=1, len=rawBodies.length; i<len; i++) {
b = rawBodies[i];
bodies[b[1][1]] = b[2];
}
rawParams = node[3];
params = {};
for (i=1, len=rawParams.length; i<len; i++) {
p = rawParams[i];
params[p[1][1]] = p[2][1];
}
ctx = node[2][1] ? node[2][1].text : null;
return compiler.pragmas[name](context, ctx, bodies, params);
},
partial: function(context, node) {
return '.partial(' +
compiler.compileNode(context, node[1]) +
',' + compiler.compileNode(context, node[2]) +
',' + compiler.compileNode(context, node[3]) + ')';
},
context: function(context, node) {
if (node[1]) {
return 'ctx.rebase(' + compiler.compileNode(context, node[1]) + ')';
}
return 'ctx';
},
params: function(context, node) {
var out = [];
for (var i=1, len=node.length; i<len; i++) {
out.push(compiler.compileNode(context, node[i]));
}
if (out.length) {
return '{' + out.join(',') + '}';
}
return '{}';
},
bodies: function(context, node) {
var out = [];
for (var i=1, len=node.length; i<len; i++) {
out.push(compiler.compileNode(context, node[i]));
}
return '{' + out.join(',') + '}';
},
param: function(context, node) {
return compiler.compileNode(context, node[1]) + ':' + compiler.compileNode(context, node[2]);
},
filters: function(context, node) {
var list = [];
for (var i=1, len=node.length; i<len; i++) {
var filter = node[i];
list.push('"' + filter + '"');
}
return '"' + context.auto + '"' +
(list.length ? ',[' + list.join(',') + ']' : '');
},
key: function(context, node) {
return 'ctx.get(["' + node[1] + '"], false)';
},
path: function(context, node) {
var current = node[1],
keys = node[2],
list = [];
for (var i=0,len=keys.length; i<len; i++) {
if (isArray(keys[i])) {
list.push(compiler.compileNode(context, keys[i]));
} else {
list.push('"' + keys[i] + '"');
}
}
return 'ctx.getPath(' + current + ', [' + list.join(',') + '])';
},
literal: function(context, node) {
return escape(node[1]);
},
raw: function(context, node) {
return ".write(" + escape(node[1]) + ")";
}
};
function compileSection(context, node, cmd) {
return '.' + cmd + '(' +
compiler.compileNode(context, node[1]) +
',' + compiler.compileNode(context, node[2]) + ',' +
compiler.compileNode(context, node[4]) + ',' +
compiler.compileNode(context, node[3]) +
')';
}
var BS = /\\/g,
DQ = /"/g,
LF = /\f/g,
NL = /\n/g,
CR = /\r/g,
TB = /\t/g;
function escapeToJsSafeString(str) {
return str.replace(BS, '\\\\')
.replace(DQ, '\\"')
.replace(LF, '\\f')
.replace(NL, '\\n')
.replace(CR, '\\r')
.replace(TB, '\\t');
}
var escape = (typeof JSON === 'undefined') ?
function(str) { return '"' + escapeToJsSafeString(str) + '"';} :
JSON.stringify;
// expose compiler methods
dust.compile = compiler.compile;
dust.filterNode = compiler.filterNode;
dust.optimizers = compiler.optimizers;
dust.pragmas = compiler.pragmas;
dust.compileNode = compiler.compileNode;
dust.nodes = compiler.nodes;
return compiler;
}));