123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- 'use strict';
-
- var nodes = require('./nodes');
-
- var lib = require('./lib');
-
- var sym = 0;
-
- function gensym() {
- return 'hole_' + sym++;
- } // copy-on-write version of map
-
-
- function mapCOW(arr, func) {
- var res = null;
-
- for (var i = 0; i < arr.length; i++) {
- var item = func(arr[i]);
-
- if (item !== arr[i]) {
- if (!res) {
- res = arr.slice();
- }
-
- res[i] = item;
- }
- }
-
- return res || arr;
- }
-
- function walk(ast, func, depthFirst) {
- if (!(ast instanceof nodes.Node)) {
- return ast;
- }
-
- if (!depthFirst) {
- var astT = func(ast);
-
- if (astT && astT !== ast) {
- return astT;
- }
- }
-
- if (ast instanceof nodes.NodeList) {
- var children = mapCOW(ast.children, function (node) {
- return walk(node, func, depthFirst);
- });
-
- if (children !== ast.children) {
- ast = new nodes[ast.typename](ast.lineno, ast.colno, children);
- }
- } else if (ast instanceof nodes.CallExtension) {
- var args = walk(ast.args, func, depthFirst);
- var contentArgs = mapCOW(ast.contentArgs, function (node) {
- return walk(node, func, depthFirst);
- });
-
- if (args !== ast.args || contentArgs !== ast.contentArgs) {
- ast = new nodes[ast.typename](ast.extName, ast.prop, args, contentArgs);
- }
- } else {
- var props = ast.fields.map(function (field) {
- return ast[field];
- });
- var propsT = mapCOW(props, function (prop) {
- return walk(prop, func, depthFirst);
- });
-
- if (propsT !== props) {
- ast = new nodes[ast.typename](ast.lineno, ast.colno);
- propsT.forEach(function (prop, i) {
- ast[ast.fields[i]] = prop;
- });
- }
- }
-
- return depthFirst ? func(ast) || ast : ast;
- }
-
- function depthWalk(ast, func) {
- return walk(ast, func, true);
- }
-
- function _liftFilters(node, asyncFilters, prop) {
- var children = [];
- var walked = depthWalk(prop ? node[prop] : node, function (descNode) {
- var symbol;
-
- if (descNode instanceof nodes.Block) {
- return descNode;
- } else if (descNode instanceof nodes.Filter && lib.indexOf(asyncFilters, descNode.name.value) !== -1 || descNode instanceof nodes.CallExtensionAsync) {
- symbol = new nodes.Symbol(descNode.lineno, descNode.colno, gensym());
- children.push(new nodes.FilterAsync(descNode.lineno, descNode.colno, descNode.name, descNode.args, symbol));
- }
-
- return symbol;
- });
-
- if (prop) {
- node[prop] = walked;
- } else {
- node = walked;
- }
-
- if (children.length) {
- children.push(node);
- return new nodes.NodeList(node.lineno, node.colno, children);
- } else {
- return node;
- }
- }
-
- function liftFilters(ast, asyncFilters) {
- return depthWalk(ast, function (node) {
- if (node instanceof nodes.Output) {
- return _liftFilters(node, asyncFilters);
- } else if (node instanceof nodes.Set) {
- return _liftFilters(node, asyncFilters, 'value');
- } else if (node instanceof nodes.For) {
- return _liftFilters(node, asyncFilters, 'arr');
- } else if (node instanceof nodes.If) {
- return _liftFilters(node, asyncFilters, 'cond');
- } else if (node instanceof nodes.CallExtension) {
- return _liftFilters(node, asyncFilters, 'args');
- } else {
- return undefined;
- }
- });
- }
-
- function liftSuper(ast) {
- return walk(ast, function (blockNode) {
- if (!(blockNode instanceof nodes.Block)) {
- return;
- }
-
- var hasSuper = false;
- var symbol = gensym();
- blockNode.body = walk(blockNode.body, function (node) {
- // eslint-disable-line consistent-return
- if (node instanceof nodes.FunCall && node.name.value === 'super') {
- hasSuper = true;
- return new nodes.Symbol(node.lineno, node.colno, symbol);
- }
- });
-
- if (hasSuper) {
- blockNode.body.children.unshift(new nodes.Super(0, 0, blockNode.name, new nodes.Symbol(0, 0, symbol)));
- }
- });
- }
-
- function convertStatements(ast) {
- return depthWalk(ast, function (node) {
- if (!(node instanceof nodes.If) && !(node instanceof nodes.For)) {
- return undefined;
- }
-
- var async = false;
- walk(node, function (child) {
- if (child instanceof nodes.FilterAsync || child instanceof nodes.IfAsync || child instanceof nodes.AsyncEach || child instanceof nodes.AsyncAll || child instanceof nodes.CallExtensionAsync) {
- async = true; // Stop iterating by returning the node
-
- return child;
- }
-
- return undefined;
- });
-
- if (async) {
- if (node instanceof nodes.If) {
- return new nodes.IfAsync(node.lineno, node.colno, node.cond, node.body, node.else_);
- } else if (node instanceof nodes.For && !(node instanceof nodes.AsyncAll)) {
- return new nodes.AsyncEach(node.lineno, node.colno, node.arr, node.name, node.body, node.else_);
- }
- }
-
- return undefined;
- });
- }
-
- function cps(ast, asyncFilters) {
- return convertStatements(liftSuper(liftFilters(ast, asyncFilters)));
- }
-
- function transform(ast, asyncFilters) {
- return cps(ast, asyncFilters || []);
- } // var parser = require('./parser');
- // var src = 'hello {% foo %}{% endfoo %} end';
- // var ast = transform(parser.parse(src, [new FooExtension()]), ['bar']);
- // nodes.printNodes(ast);
-
-
- module.exports = {
- transform: transform
- };
|