Improve block parsing to handle args/kwargs on subblocks

This commit is contained in:
Samy Pessé
2016-04-13 16:22:52 +02:00
parent ef2a24fe6f
commit ce7de93936
+68 -50
View File
@@ -10,6 +10,8 @@ var defaultBlocks = require('./blocks');
var defaultFilters = require('./filters');
var Loader = require('./loader');
var NODE_ENDARGS = '%%endargs%%';
// Return extension name for a specific block
function blockExtName(name) {
return 'Block'+name+'Extension';
@@ -21,6 +23,12 @@ function normBlockResult(blk) {
return blk;
}
// Extract kwargs from an arguments array
function extractKwargs(args) {
var last = _.last(args)
return (_.isObject(last) && last.__keywords)? args.pop() : {};
}
function TemplateEngine(output) {
this.output = output;
this.book = output.book;
@@ -182,87 +190,97 @@ TemplateEngine.prototype.addBlock = function(name, block) {
var lastBlockName = null;
var lastBlockArgs = null;
var allBlocks = block.blocks.concat([block.end]);
var subbodies = {};
// Parse first block
var tok = parser.nextToken();
var args = parser.parseSignature(null, true);
lastBlockArgs = parser.parseSignature(null, true);
parser.advanceAfterBlockEnd(tok.value);
var args = new nodes.NodeList();
var bodies = [];
var blockNamesNode = new nodes.Array(tok.lineno, tok.colno);
var blockArgCounts = new nodes.Array(tok.lineno, tok.colno);
// Parse while we found "end<block>"
do {
// Read body
var currentBody = parser.parseUntilBlocks.apply(parser, allBlocks);
// Handle body with previous block name and args
if (lastBlockName) {
subbodies[lastBlockName] = subbodies[lastBlockName] || [];
subbodies[lastBlockName].push({
body: currentBody,
args: lastBlockArgs
});
} else {
body = currentBody;
}
blockNamesNode.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockName));
blockArgCounts.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockArgs.children.length));
bodies.push(currentBody);
// Append arguments of this block as arguments of the run function
_.each(lastBlockArgs.children, function(child) {
args.addChild(child);
});
// Read new block
lastBlockName = parser.peekToken().value;
lastBlockName = parser.nextToken().value;
// Parse signature and move to the end of the block
if (lastBlockName != block.end) {
lastBlockArgs = parser.parseSignature(null, true);
parser.advanceAfterBlockEnd(lastBlockName);
}
parser.advanceAfterBlockEnd(lastBlockName);
} while (lastBlockName != block.end);
parser.advanceAfterBlockEnd();
var bodies = [body];
_.each(block.blocks, function(blockName) {
subbodies[blockName] = subbodies[blockName] || [];
if (subbodies[blockName].length === 0) {
subbodies[blockName].push({
args: new nodes.NodeList(),
body: new nodes.NodeList()
});
}
bodies.push(subbodies[blockName][0].body);
});
args.addChild(blockNamesNode);
args.addChild(blockArgCounts);
args.addChild(new nodes.Literal(args.lineno, args.colno, NODE_ENDARGS));
return new nodes.CallExtensionAsync(this, 'run', args, bodies);
};
this.run = function(context) {
var args = Array.prototype.slice.call(arguments, 1);
var callback = args.pop();
var fnArgs = Array.prototype.slice.call(arguments, 1);
// Extract blocks
var blocks = args
.concat([])
.slice(-block.blocks.length);
var args;
var blocks = [];
var bodies = [];
var blockNames;
var blockArgCounts;
var callback;
// Eliminate blocks from list
if (block.blocks.length > 0) args = args.slice(0, -block.blocks.length);
// Extract callback
callback = fnArgs.pop();
// Extract main body and kwargs
var body = args.pop();
var kwargs = _.isObject(_.last(args))? args.pop() : {};
// Detect end of arguments
var endArgIndex = fnArgs.indexOf(NODE_ENDARGS);
// Extract blocks body
var _blocks = _.map(block.blocks, function(blockName, i){
return {
name: blockName,
body: blocks[i]()
};
// Extract arguments and bodies
args = fnArgs.slice(0, endArgIndex);
bodies = fnArgs.slice(endArgIndex + 1);
// Extract block counts
blockArgCounts = args.pop();
blockNames = args.pop();
// Recreate list of blocks
_.each(blockNames, function(name, i) {
var countArgs = blockArgCounts[i];
var blockBody = bodies.shift();
var blockArgs = countArgs > 0? args.slice(0, countArgs) : [];
args = args.slice(countArgs);
var blockKwargs = extractKwargs(blockArgs);
blocks.push({
name: name,
body: blockBody(),
args: blockArgs,
kwargs: blockKwargs
});
});
var mainBlock = blocks.shift();
mainBlock.blocks = blocks;
Promise()
.then(function() {
return that.applyBlock(name, {
body: body(),
args: args,
kwargs: kwargs,
blocks: _blocks
}, context);
return that.applyBlock(name, mainBlock, context);
})
// Process the block returned