diff --git a/lib/lang/DefaultTags.js b/lib/lang/DefaultTags.js index 902afe3..dd9ae56 100644 --- a/lib/lang/DefaultTags.js +++ b/lib/lang/DefaultTags.js @@ -1,598 +1,604 @@ -var __extends = this.__extends || function (d, b) { - function __() { this.constructor = d; } - __.prototype = b.prototype; - d.prototype = new __(); -}; -var ExpressionParser = require('../parser/ExpressionParser') -var ParserNode = require('../parser/ParserNode') -var TemplateParser = require('../parser/TemplateParser') - - -function checkNoMoreTokens(expressionTokenReader) { - if(expressionTokenReader.hasMore()) { - throw (new Error("Unexpected token '" + JSON.stringify(expressionTokenReader.peek()) + "'")); - } - return expressionTokenReader; -} -function _flowexception(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - throw (new TemplateParser.FlowException(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader)); -} -function handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, handlers, innerNodeHandler) { - while(true) { - try { - var keys = []; - for(var key in handlers) { - keys.push(key); - } - var node = templateParser.parseTemplateSyncOne(tokenParserContext, templateTokenReader); - if(node == null) { - throw (new Error("Unexpected end of '" + blockType + "' no any of [" + keys.map(function (key) { - return "'" + key + "'"; - }).join(', ') + "]")); - } - innerNodeHandler(node); - } catch (e) { - if(!(e instanceof TemplateParser.FlowException)) { - throw (e); - } - var handler = handlers[e.blockType]; - if(handler !== undefined) { - if(handler(e)) { - return; - } - } else { - throw (new Error("Unexpected '" + e.blockType + "' for '" + blockType + "'")); - } - } - } -} -var ParserNodeAutoescape = (function (_super) { - __extends(ParserNodeAutoescape, _super); - function ParserNodeAutoescape(expression, inner) { - _super.call(this); - this.expression = expression; - this.inner = inner; - } - ParserNodeAutoescape.prototype.generateCode = function () { - return ('runtimeContext.autoescape(' + this.expression.generateCode() + ', function() {' + this.inner.generateCode() + '}, true);'); - }; - return ParserNodeAutoescape; -})(ParserNode.ParserNodeStatement); -exports.ParserNodeAutoescape = ParserNodeAutoescape; -var ParserNodeExpressionFilter = (function (_super) { - __extends(ParserNodeExpressionFilter, _super); - function ParserNodeExpressionFilter(inner) { - _super.call(this); - this.inner = inner; - this.filters = []; - } - ParserNodeExpressionFilter.prototype.addFilter = function (filterName, filterParameters) { - this.filters.push({ - name: filterName, - parameters: filterParameters - }); - }; - ParserNodeExpressionFilter.prototype.generateCode = function () { - var out = ''; - this.filters.reverse().forEach(function (filter) { - out += 'runtimeContext.filter(' + JSON.stringify(filter.name) + ', ['; - }); - out += 'runtimeContext.captureOutput(function () {'; - out += this.inner.generateCode(); - out += '})'; - this.filters.reverse().forEach(function (filter) { - if(filter.parameters && filter.parameters.expressions.length > 0) { - out += ','; - out += filter.parameters.generateCode(); - } - out += '])'; - }); - return out; - }; - return ParserNodeExpressionFilter; -})(ParserNode.ParserNodeExpression); -exports.ParserNodeExpressionFilter = ParserNodeExpressionFilter; -var ParserNodeScopeSet = (function (_super) { - __extends(ParserNodeScopeSet, _super); - function ParserNodeScopeSet(key, value) { - _super.call(this); - this.key = key; - this.value = value; - } - ParserNodeScopeSet.prototype.generateCode = function () { - return 'runtimeContext.scope.set(' + JSON.stringify(this.key) + ', ' + this.value.generateCode() + ');'; - }; - return ParserNodeScopeSet; -})(ParserNode.ParserNodeStatement); -exports.ParserNodeScopeSet = ParserNodeScopeSet; -var ParserNodeIf = (function (_super) { - __extends(ParserNodeIf, _super); - function ParserNodeIf() { - _super.apply(this, arguments); - - this.conditions = []; - } - ParserNodeIf.prototype.addCaseCondition = function (expression) { - this.conditions.push({ - expression: expression, - code: new ParserNode.ParserNodeContainer() - }); - }; - ParserNodeIf.prototype.addElseCondition = function () { - this.conditions.push({ - expression: null, - code: new ParserNode.ParserNodeContainer() - }); - }; - ParserNodeIf.prototype.addCodeToCondition = function (node) { - this.conditions[this.conditions.length - 1].code.add(node); - }; - ParserNodeIf.prototype.generateCode = function () { - var out = ''; - for(var n = 0; n < this.conditions.length; n++) { - var condition = this.conditions[n]; - if(out != '') { - out += 'else '; - } - if(condition.expression != null) { - out += 'if (' + condition.expression.generateCode() + ')'; - } - out += '{ '; - out += condition.code.generateCode(); - out += '}'; - } - return out; - }; - return ParserNodeIf; -})(ParserNode.ParserNodeStatement); -exports.ParserNodeIf = ParserNodeIf; -var ParserNodeFor = (function (_super) { - __extends(ParserNodeFor, _super); - function ParserNodeFor(keyId, condId, valueId, nodeList, forCode, elseCode) { - _super.call(this); - this.keyId = keyId; - this.condId = condId; - this.valueId = valueId; - this.nodeList = nodeList; - this.forCode = forCode; - this.elseCode = elseCode; - } - ParserNodeFor.prototype.generateCode = function () { - var out = ''; - out += ('runtimeContext.createScope((function() { '); - out += (' var list = ' + this.nodeList.generateCode() + ';'); - out += (' if (!runtimeContext.emptyList(list)) {'); - out += (' runtimeContext.each(list, function(k, v) { '); - out += (' ' + (new ParserNode.ParserNodeAssignment(this.valueId, new ParserNode.ParserNodeRaw("v"))).generateCode() + ';'); - if(this.keyId !== undefined) { - out += (' ' + (new ParserNode.ParserNodeAssignment(this.keyId, new ParserNode.ParserNodeRaw("k"))).generateCode() + ';'); - } - if(this.condId) { - out += (' if (' + this.condId.generateCode() + ') { '); - } else { - out += (' if (true) { '); - } - out += this.forCode.generateCode(); - out += ('}'); - out += (' });'); - out += ('} else {'); - { - out += this.elseCode.generateCode(); - } - out += ('} '); - out += ('}));'); - return out; - }; - return ParserNodeFor; -})(ParserNode.ParserNodeStatement); -exports.ParserNodeFor = ParserNodeFor; -var DefaultTags = (function () { - function DefaultTags() { } - DefaultTags.endautoescape = _flowexception; - DefaultTags.autoescape = function autoescape(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); - checkNoMoreTokens(expressionTokenReader); - var innerNode = new ParserNode.ParserNodeContainer(); - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'endautoescape': function (e) { - return true; - } - }, function (node) { - innerNode.add(node); - }); - return new ParserNodeAutoescape(expressionNode, innerNode); - }; - DefaultTags.set = function set(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); - var nodeIds = expressionParser.parseIdentifierCommaList(); - expressionTokenReader.expectAndMoveNext([ - '=' - ]); - var nodeValues = expressionParser.parseCommaExpression(); - checkNoMoreTokens(expressionTokenReader); - if(nodeIds.length != nodeValues.expressions.length) { - throw (new Error("variables doesn't match values")); - } - var container = new ParserNode.ParserNodeContainer(); - for(var n = 0; n < nodeIds.length; n++) { - container.add(new ParserNodeScopeSet(String((nodeIds[n]).value), nodeValues.expressions[n])); - } - return container; - }; - DefaultTags.$do = function $do(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); - checkNoMoreTokens(expressionTokenReader); - return new ParserNode.ParserNodeStatementExpression(expressionNode); - }; - DefaultTags.endembed = _flowexception; - DefaultTags.embed = function embed(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionString = expressionTokenReader.getSliceWithCallback(function () { - var includeName = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); - }).map(function (item) { - return item.rawValue; - }); - checkNoMoreTokens(expressionTokenReader); - var offsetStart = templateTokenReader.getOffset(); - var offsetEnd = offsetStart; - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'endembed': function (e) { - offsetEnd = templateTokenReader.getOffset() - 1; - return true; - } - }, function (node) { - }); - var rawText = templateTokenReader.getSlice(offsetStart, offsetEnd).map(function (item) { - return (item).rawText; - }).join(''); - var templateString = '{% extends ' + expressionString + ' %}' + rawText; - return new ParserNode.ParserNodeRaw('runtimeContext.include(runtimeContext.compileString(' + JSON.stringify(templateString) + '));'); - }; - DefaultTags.endfilter = _flowexception; - DefaultTags.filter = function filter(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var innerNode = new ParserNode.ParserNodeContainer(); - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'endfilter': function (e) { - return true; - } - }, function (node) { - innerNode.add(node); - }); - var filterNode = new ParserNodeExpressionFilter(innerNode); - var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); - while(true) { - var filterName = (expressionParser.parseIdentifier()).value; - var parameters = null; - if(expressionTokenReader.checkAndMoveNext([ - '(' - ])) { - parameters = expressionParser.parseCommaExpression(); - expressionTokenReader.expectAndMoveNext([ - ')' - ]); - } - filterNode.addFilter(filterName, parameters); - if(!expressionTokenReader.checkAndMoveNext([ - '|' - ])) { - break; - } - } - checkNoMoreTokens(expressionTokenReader); - return new ParserNode.ParserNodeStatementExpression(new ParserNode.ParserNodeOutputNodeExpression(filterNode)); - }; - DefaultTags.flush = function flush(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - }; - DefaultTags.endmacro = _flowexception; - DefaultTags.macro = function macro(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); - var macroName = expressionTokenReader.read().value; - var paramNames = []; - expressionTokenReader.expectAndMoveNext([ - '(' - ]); - if(expressionTokenReader.peek().value != ")") { - while(true) { - paramNames.push(expressionParser.parseIdentifier()); - if(expressionTokenReader.expectAndMoveNext([ - ')', - ',' - ]) == ')') { - break; - } - } - } else { - expressionTokenReader.expectAndMoveNext([ - ')' - ]); - } - checkNoMoreTokens(expressionTokenReader); - var macroNode = new ParserNode.ParserNodeContainer(); - macroNode.add(new ParserNode.ParserNodeRaw('var _arguments = arguments;')); - macroNode.add(new ParserNode.ParserNodeRaw('return runtimeContext.captureOutput(function() { ')); - macroNode.add(new ParserNode.ParserNodeRaw('return runtimeContext.autoescape(false, function() { ')); - macroNode.add(new ParserNode.ParserNodeRaw('runtimeContext.createScope(function() { ')); - paramNames.forEach(function (paramName, index) { - var assign = new ParserNode.ParserNodeAssignment(paramName, new ParserNode.ParserNodeRaw('_arguments[' + index + ']')); - macroNode.add(new ParserNode.ParserNodeStatementExpression(assign)); - }); - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'endmacro': function (e) { - return true; - } - }, function (node) { - macroNode.add(node); - }); - macroNode.add(new ParserNode.ParserNodeRaw('});')); - macroNode.add(new ParserNode.ParserNodeRaw('});')); - macroNode.add(new ParserNode.ParserNodeRaw('});')); - var macroCode = tokenParserContext.setMacro(macroName, macroNode); - return new ParserNode.ParserNodeRaw(''); - }; - DefaultTags.from = function from(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); - var fileNameNode = expressionParser.parseExpression(); - expressionTokenReader.expectAndMoveNext([ - 'import' - ]); - var pairs = []; - while(expressionTokenReader.peek().value != null) { - var fromNode = expressionTokenReader.read().value; - var toNode = fromNode; - var token = expressionTokenReader.expectAndMoveNext([ - 'as', - ',', - null - ]); - if(token == 'as') { - toNode = expressionTokenReader.read().value; - expressionTokenReader.expectAndMoveNext([ - ',', - null - ]); - } - pairs.push([ - fromNode, - toNode - ]); - } - checkNoMoreTokens(expressionTokenReader); - return new ParserNode.ParserNodeContainer([ - new ParserNode.ParserNodeRaw('runtimeContext.fromImport('), - fileNameNode, - new ParserNode.ParserNodeRaw(', ' + JSON.stringify(pairs) + ');') - ]); - }; - DefaultTags.$import = function $import(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); - var fileNameNode = expressionParser.parseExpression(); - expressionTokenReader.expectAndMoveNext([ - 'as' - ]); - var aliasNode = expressionParser.parseIdentifier(); - checkNoMoreTokens(expressionTokenReader); - return new ParserNode.ParserNodeStatementExpression(new ParserNode.ParserNodeAssignment(aliasNode, new ParserNode.ParserNodeRaw('runtimeContext.import(' + fileNameNode.generateCode() + ')'))); - }; - DefaultTags.use = function use(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); - var fileName = expressionParser.parseStringLiteral().value; - var pairs = { - }; - while(expressionTokenReader.checkAndMoveNext([ - 'with' - ])) { - var fromNode = expressionParser.parseIdentifierOnly().value; - expressionTokenReader.expectAndMoveNext([ - 'as' - ]); - var toNode = expressionParser.parseIdentifierOnly().value; - pairs['block_' + fromNode] = 'block_' + toNode; - } - checkNoMoreTokens(expressionTokenReader); - var info = templateParser.getEvalCode(fileName, tokenParserContext.common); - info.tokenParserContext.iterateBlocks(function (node, name) { - if(name.match(/^block_/)) { - if(pairs[name]) { - tokenParserContext.setBlock(pairs[name], node); - } else { - tokenParserContext.setBlock(name, node); - } - } - }); - return new ParserNode.ParserNodeRaw(''); - }; - DefaultTags.include = function include(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var node = new ParserNode.ParserNodeContainer(); - node.add(new ParserNode.ParserNodeRaw('runtimeContext.include(')); - var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); - node.add(expressionNode); - if(expressionTokenReader.checkAndMoveNext([ - 'with' - ])) { - node.add(new ParserNode.ParserNodeRaw(',')); - node.add((new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression()); - } else { - node.add(new ParserNode.ParserNodeRaw(', undefined')); - } - if(expressionTokenReader.checkAndMoveNext([ - 'only' - ])) { - node.add(new ParserNode.ParserNodeRaw(', true')); - } else { - node.add(new ParserNode.ParserNodeRaw(', false')); - } - node.add(new ParserNode.ParserNodeRaw(', ' + JSON.stringify(tokenParserContext.common.serialize()))); - checkNoMoreTokens(expressionTokenReader); - node.add(new ParserNode.ParserNodeRaw(');')); - return node; - }; - DefaultTags.endraw = _flowexception; - DefaultTags.endverbatim = _flowexception; - DefaultTags.raw = function raw(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - checkNoMoreTokens(expressionTokenReader); - var offsetStart = templateTokenReader.getOffset(); - var offsetEnd = offsetStart; - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'endverbatim': function (e) { - offsetEnd = templateTokenReader.getOffset() - 1; - return true; - }, - 'endraw': function (e) { - offsetEnd = templateTokenReader.getOffset() - 1; - return true; - } - }, function (node) { - }); - var rawText = templateTokenReader.getSlice(offsetStart, offsetEnd).map(function (item) { - return (item).rawText; - }).join(''); - return new ParserNode.ParserNodeOutputText(rawText); - }; - DefaultTags.verbatim = DefaultTags.raw; - DefaultTags.endsandbox = _flowexception; - DefaultTags.sandbox = function sandbox(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - checkNoMoreTokens(expressionTokenReader); - var innerNode = new ParserNode.ParserNodeContainer(); - tokenParserContext.common.setSandbox(function () { - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'endsandbox': function (e) { - return true; - } - }, function (node) { - innerNode.add(node); - }); - }); - return new ParserNode.ParserNodeContainer([ - new ParserNode.ParserNodeRaw('runtimeContext.createScope(function() { '), - new ParserNode.ParserNodeRaw(' runtimeContext.scopeSet("__sandboxed", true);'), - innerNode, - new ParserNode.ParserNodeRaw('});') - ]); - }; - DefaultTags.endspaceless = _flowexception; - DefaultTags.spaceless = function spaceless(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - checkNoMoreTokens(expressionTokenReader); - var innerNode = new ParserNode.ParserNodeContainer(); - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'endspaceless': function (e) { - return true; - } - }, function (node) { - innerNode.add(node); - }); - return new ParserNode.ParserNodeStatementExpression(new ParserNode.ParserNodeOutputNodeExpression(new ParserNode.ParserNodeContainer([ - new ParserNode.ParserNodeRaw('runtimeContext.filter("spaceless", [runtimeContext.captureOutput(function() { '), - innerNode, - new ParserNode.ParserNodeRaw('})])') - ]))); - }; - DefaultTags.$else = _flowexception; - DefaultTags.$elseif = _flowexception; - DefaultTags.$endif = _flowexception; - DefaultTags.$if = function $if(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var didElse = false; - var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); - checkNoMoreTokens(expressionTokenReader); - var parserNodeIf = new ParserNodeIf(); - parserNodeIf.addCaseCondition(expressionNode); - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'elseif': function (e) { - if(didElse) { - throw (new Error("Can't put 'elseif' after the 'else'")); - } - var expressionNode = (new ExpressionParser.ExpressionParser(e.expressionTokenReader, tokenParserContext)).parseExpression(); - checkNoMoreTokens(expressionTokenReader); - parserNodeIf.addCaseCondition(expressionNode); - }, - 'else': function (e) { - if(didElse) { - throw (new Error("Can't have two 'else'")); - } - parserNodeIf.addElseCondition(); - didElse = true; - }, - 'endif': function (e) { - return true; - } - }, function (node) { - parserNodeIf.addCodeToCondition(node); - }); - return parserNodeIf; - }; - DefaultTags.endblock = _flowexception; - DefaultTags.block = function block(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var blockName = 'block_' + expressionTokenReader.read().value; - var innerNode = new ParserNode.ParserNodeContainer(); - if(expressionTokenReader.hasMore()) { - var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); - checkNoMoreTokens(expressionTokenReader); - innerNode.add(new ParserNode.ParserNodeReturnStatement(new ParserNode.ParserNodeWriteExpression(expressionNode))); - } else { - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'endblock': function (e) { - return true; - } - }, function (node) { - innerNode.add(node); - }); - } - tokenParserContext.setBlock(blockName, innerNode); - return new ParserNode.ParserNodeRaw('runtimeContext.putBlock(' + JSON.stringify(blockName) + ');'); - }; - DefaultTags.$extends = function $extends(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); - checkNoMoreTokens(expressionTokenReader); - return new ParserNode.ParserNodeContainer([ - new ParserNode.ParserNodeRaw('return runtimeContext.extends('), - expressionNode, - new ParserNode.ParserNodeRaw(');') - ]); - }; - DefaultTags.$endfor = _flowexception; - DefaultTags.$for = function $for(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - var didElse = false; - var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); - var valueId = expressionParser.parseIdentifier(); - var keyId = undefined; - var condId = undefined; - var res = expressionTokenReader.expectAndMoveNext([ - ',', - 'in' - ]); - if(res == ',') { - keyId = valueId; - valueId = expressionParser.parseIdentifier(); - expressionTokenReader.expectAndMoveNext([ - 'in' - ]); - } - var nodeList = expressionParser.parseExpression(); - if(expressionTokenReader.checkAndMoveNext([ - 'if' - ])) { - condId = expressionParser.parseExpression(); - } - checkNoMoreTokens(expressionTokenReader); - var forCode = new ParserNode.ParserNodeContainer(); - var elseCode = new ParserNode.ParserNodeContainer(); - handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { - 'else': function (e) { - if(didElse) { - throw (new Error("Can't have two 'else'")); - } - didElse = true; - }, - 'endfor': function (e) { - return true; - } - }, function (node) { - if(!didElse) { - forCode.add(node); - } else { - elseCode.add(node); - } - }); - return new ParserNodeFor(keyId, condId, valueId, nodeList, forCode, elseCode); - }; - return DefaultTags; -})(); -exports.DefaultTags = DefaultTags; +var __extends = this.__extends || function (d, b) { + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var ExpressionParser = require('../parser/ExpressionParser') +var ParserNode = require('../parser/ParserNode') +var TemplateParser = require('../parser/TemplateParser') + + +function checkNoMoreTokens(expressionTokenReader) { + if(expressionTokenReader.hasMore()) { + throw (new Error("Unexpected token '" + JSON.stringify(expressionTokenReader.peek()) + "'")); + } + return expressionTokenReader; +} +function _flowexception(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + throw (new TemplateParser.FlowException(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader)); +} +function handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, handlers, innerNodeHandler) { + while(true) { + try { + var keys = []; + for(var key in handlers) { + keys.push(key); + } + var node = templateParser.parseTemplateSyncOne(tokenParserContext, templateTokenReader); + if(node == null) { + throw (new Error("Unexpected end of '" + blockType + "' no any of [" + keys.map(function (key) { + return "'" + key + "'"; + }).join(', ') + "]")); + } + innerNodeHandler(node); + } catch (e) { + if(!(e instanceof TemplateParser.FlowException)) { + throw (e); + } + var handler = handlers[e.blockType]; + if(handler !== undefined) { + if(handler(e)) { + return; + } + } else { + throw (new Error("Unexpected '" + e.blockType + "' for '" + blockType + "'")); + } + } + } +} +var ParserNodeAutoescape = (function (_super) { + __extends(ParserNodeAutoescape, _super); + function ParserNodeAutoescape(expression, inner) { + _super.call(this); + this.expression = expression; + this.inner = inner; + } + ParserNodeAutoescape.prototype.generateCode = function (context) { + return ('runtimeContext.autoescape(' + this.expression.generateCode(context) + ', function() {' + this.inner.generateCode(context) + '}, true);'); + }; + return ParserNodeAutoescape; +})(ParserNode.ParserNodeStatement); +exports.ParserNodeAutoescape = ParserNodeAutoescape; +var ParserNodeExpressionFilter = (function (_super) { + __extends(ParserNodeExpressionFilter, _super); + function ParserNodeExpressionFilter(inner) { + _super.call(this); + this.inner = inner; + this.filters = []; + } + ParserNodeExpressionFilter.prototype.addFilter = function (filterName, filterParameters) { + this.filters.push({ + name: filterName, + parameters: filterParameters + }); + }; + ParserNodeExpressionFilter.prototype.generateCode = function (context) { + var out = ''; + this.filters.reverse().forEach(function (filter) { + out += 'runtimeContext.filter(' + JSON.stringify(filter.name) + ', ['; + }); + out += 'runtimeContext.captureOutput(function () {'; + out += this.inner.generateCode(context); + out += '})'; + this.filters.reverse().forEach(function (filter) { + if(filter.parameters && filter.parameters.expressions.length > 0) { + out += ','; + out += filter.parameters.generateCode(context); + } + out += '])'; + }); + return out; + }; + return ParserNodeExpressionFilter; +})(ParserNode.ParserNodeExpression); +exports.ParserNodeExpressionFilter = ParserNodeExpressionFilter; +var ParserNodeScopeSet = (function (_super) { + __extends(ParserNodeScopeSet, _super); + function ParserNodeScopeSet(key, value) { + _super.call(this); + this.key = key; + this.value = value; + } + ParserNodeScopeSet.prototype.generateCode = function (context) { + return 'runtimeContext.scope.set(' + JSON.stringify(this.key) + ', ' + this.value.generateCode(context) + ');'; + }; + return ParserNodeScopeSet; +})(ParserNode.ParserNodeStatement); +exports.ParserNodeScopeSet = ParserNodeScopeSet; +var ParserNodeIf = (function (_super) { + __extends(ParserNodeIf, _super); + function ParserNodeIf() { + _super.apply(this, arguments); + + this.conditions = []; + } + ParserNodeIf.prototype.addCaseCondition = function (expression) { + this.conditions.push({ + expression: expression, + code: new ParserNode.ParserNodeContainer() + }); + }; + ParserNodeIf.prototype.addElseCondition = function () { + this.conditions.push({ + expression: null, + code: new ParserNode.ParserNodeContainer() + }); + }; + ParserNodeIf.prototype.addCodeToCondition = function (node) { + this.conditions[this.conditions.length - 1].code.add(node); + }; + ParserNodeIf.prototype.generateCode = function (context) { + var out = ''; + for(var n = 0; n < this.conditions.length; n++) { + var condition = this.conditions[n]; + if(out != '') { + out += 'else '; + } + if(condition.expression != null) { + out += 'if (' + condition.expression.generateCode(context) + ')'; + } + out += '{ '; + out += condition.code.generateCode(context); + out += '}'; + } + return out; + }; + return ParserNodeIf; +})(ParserNode.ParserNodeStatement); +exports.ParserNodeIf = ParserNodeIf; +var ParserNodeFor = (function (_super) { + __extends(ParserNodeFor, _super); + function ParserNodeFor(keyId, condId, valueId, nodeList, forCode, elseCode) { + _super.call(this); + this.keyId = keyId; + this.condId = condId; + this.valueId = valueId; + this.nodeList = nodeList; + this.forCode = forCode; + this.elseCode = elseCode; + } + ParserNodeFor.prototype.generateCode = function (context) { + var out = ''; + out += ('runtimeContext.createScope((function() { '); + out += (' var list = ' + this.nodeList.generateCode(context) + ';'); + out += (' if (!runtimeContext.emptyList(list)) {'); + out += (' runtimeContext.each(list, function(k, v) { '); + out += (' ' + (new ParserNode.ParserNodeAssignment(this.valueId, new ParserNode.ParserNodeRaw("v"))).generateCode(context) + ';'); + if(this.keyId !== undefined) { + out += (' ' + (new ParserNode.ParserNodeAssignment(this.keyId, new ParserNode.ParserNodeRaw("k"))).generateCode(context) + ';'); + } + if(this.condId) { + out += (' if (' + this.condId.generateCode() + ') { '); + } else { + out += (' if (true) { '); + } + out += this.forCode.generateCode(context); + out += ('}'); + out += (' });'); + out += ('} else {'); + { + out += this.elseCode.generateCode(context); + } + out += ('} '); + out += ('}));'); + return out; + }; + return ParserNodeFor; +})(ParserNode.ParserNodeStatement); +exports.ParserNodeFor = ParserNodeFor; +var DefaultTags = (function () { + function DefaultTags() { } + DefaultTags.endautoescape = _flowexception; + DefaultTags.autoescape = function autoescape(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); + checkNoMoreTokens(expressionTokenReader); + var innerNode = new ParserNode.ParserNodeContainer(); + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'endautoescape': function (e) { + return true; + } + }, function (node) { + innerNode.add(node); + }); + return new ParserNodeAutoescape(expressionNode, innerNode); + }; + DefaultTags.set = function set(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); + var nodeIds = expressionParser.parseIdentifierCommaList(); + expressionTokenReader.expectAndMoveNext([ + '=' + ]); + var nodeValues = expressionParser.parseCommaExpression(); + checkNoMoreTokens(expressionTokenReader); + if(nodeIds.length != nodeValues.expressions.length) { + throw (new Error("variables doesn't match values")); + } + var container = new ParserNode.ParserNodeContainer(); + for(var n = 0; n < nodeIds.length; n++) { + container.add(new ParserNodeScopeSet(String((nodeIds[n]).value), nodeValues.expressions[n])); + } + return container; + }; + DefaultTags.$do = function $do(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); + checkNoMoreTokens(expressionTokenReader); + return new ParserNode.ParserNodeStatementExpression(expressionNode); + }; + DefaultTags.endembed = _flowexception; + DefaultTags.embed = function embed(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionString = expressionTokenReader.getSliceWithCallback(function () { + var includeName = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); + }).map(function (item) { + return item.rawValue; + }); + checkNoMoreTokens(expressionTokenReader); + var offsetStart = templateTokenReader.getOffset(); + var offsetEnd = offsetStart; + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'endembed': function (e) { + offsetEnd = templateTokenReader.getOffset() - 1; + return true; + } + }, function (node) { + }); + var rawText = templateTokenReader.getSlice(offsetStart, offsetEnd).map(function (item) { + return (item).rawText; + }).join(''); + var templateString = '{% extends ' + expressionString + ' %}' + rawText; + return new ParserNode.ParserNodeRaw('runtimeContext.include(runtimeContext.compileString(' + JSON.stringify(templateString) + '));'); + }; + DefaultTags.endfilter = _flowexception; + DefaultTags.filter = function filter(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var innerNode = new ParserNode.ParserNodeContainer(); + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'endfilter': function (e) { + return true; + } + }, function (node) { + innerNode.add(node); + }); + var filterNode = new ParserNodeExpressionFilter(innerNode); + var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); + while(true) { + var filterName = (expressionParser.parseIdentifier()).value; + var parameters = null; + if(expressionTokenReader.checkAndMoveNext([ + '(' + ])) { + parameters = expressionParser.parseCommaExpression(); + expressionTokenReader.expectAndMoveNext([ + ')' + ]); + } + filterNode.addFilter(filterName, parameters); + if(!expressionTokenReader.checkAndMoveNext([ + '|' + ])) { + break; + } + } + checkNoMoreTokens(expressionTokenReader); + return new ParserNode.ParserNodeStatementExpression(new ParserNode.ParserNodeOutputNodeExpression(filterNode)); + }; + DefaultTags.flush = function flush(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + }; + DefaultTags.endmacro = _flowexception; + DefaultTags.macro = function macro(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); + var macroName = expressionTokenReader.read().value; + var paramNames = []; + expressionTokenReader.expectAndMoveNext([ + '(' + ]); + if(expressionTokenReader.peek().value != ")") { + while(true) { + paramNames.push(expressionParser.parseIdentifier()); + if(expressionTokenReader.expectAndMoveNext([ + ')', + ',' + ]) == ')') { + break; + } + } + } else { + expressionTokenReader.expectAndMoveNext([ + ')' + ]); + } + checkNoMoreTokens(expressionTokenReader); + var macroNode = new ParserNode.ParserNodeContainer(); + macroNode.add(new ParserNode.ParserNodeRaw('var _arguments = arguments;')); + macroNode.add(new ParserNode.ParserNodeRaw('return runtimeContext.captureOutput(function() { ')); + macroNode.add(new ParserNode.ParserNodeRaw('return runtimeContext.autoescape(false, function() { ')); + macroNode.add(new ParserNode.ParserNodeRaw('runtimeContext.createScope(function() { ')); + paramNames.forEach(function (paramName, index) { + var assign = new ParserNode.ParserNodeAssignment(paramName, new ParserNode.ParserNodeRaw('_arguments[' + index + ']')); + macroNode.add(new ParserNode.ParserNodeStatementExpression(assign)); + }); + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'endmacro': function (e) { + return true; + } + }, function (node) { + macroNode.add(node); + }); + macroNode.add(new ParserNode.ParserNodeRaw('});')); + macroNode.add(new ParserNode.ParserNodeRaw('});')); + macroNode.add(new ParserNode.ParserNodeRaw('});')); + var macroCode = tokenParserContext.setMacro(macroName, macroNode); + return new ParserNode.ParserNodeRaw(''); + }; + DefaultTags.from = function from(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); + var fileNameNode = expressionParser.parseExpression(); + expressionTokenReader.expectAndMoveNext([ + 'import' + ]); + var pairs = []; + while(expressionTokenReader.peek().value != null) { + var fromNode = expressionTokenReader.read().value; + var toNode = fromNode; + var token = expressionTokenReader.expectAndMoveNext([ + 'as', + ',', + null + ]); + if(token == 'as') { + toNode = expressionTokenReader.read().value; + expressionTokenReader.expectAndMoveNext([ + ',', + null + ]); + } + pairs.push([ + fromNode, + toNode + ]); + } + checkNoMoreTokens(expressionTokenReader); + return new ParserNode.ParserNodeContainer([ + new ParserNode.ParserNodeRaw('runtimeContext.fromImport('), + fileNameNode, + new ParserNode.ParserNodeRaw(', ' + JSON.stringify(pairs) + ');') + ]); + }; + DefaultTags.$import = function $import(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); + var fileNameNode = expressionParser.parseExpression(); + expressionTokenReader.expectAndMoveNext([ + 'as' + ]); + var aliasNode = expressionParser.parseIdentifier(); + checkNoMoreTokens(expressionTokenReader); + return new ParserNode.ParserNodeStatementExpression(new ParserNode.ParserNodeAssignment(aliasNode, new ParserNode.ParserNodeContainerExpression([ + new ParserNode.ParserNodeRaw('runtimeContext.import('), + fileNameNode, + new ParserNode.ParserNodeRaw(')'), + + ]))); + }; + DefaultTags.use = function use(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); + var fileName = expressionParser.parseStringLiteral().value; + var pairs = { + }; + while(expressionTokenReader.checkAndMoveNext([ + 'with' + ])) { + var fromNode = expressionParser.parseIdentifierOnly().value; + expressionTokenReader.expectAndMoveNext([ + 'as' + ]); + var toNode = expressionParser.parseIdentifierOnly().value; + pairs['block_' + fromNode] = 'block_' + toNode; + } + checkNoMoreTokens(expressionTokenReader); + var info = templateParser.getEvalCode(fileName, tokenParserContext.common); + info.tokenParserContext.iterateBlocks(function (node, name) { + if(name.match(/^block_/)) { + if(pairs[name]) { + tokenParserContext.setBlock(pairs[name], node); + } else { + tokenParserContext.setBlock(name, node); + } + } + }); + return new ParserNode.ParserNodeRaw(''); + }; + DefaultTags.include = function include(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var node = new ParserNode.ParserNodeContainer(); + node.add(new ParserNode.ParserNodeRaw('runtimeContext.include(')); + var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); + node.add(expressionNode); + if(expressionTokenReader.checkAndMoveNext([ + 'with' + ])) { + node.add(new ParserNode.ParserNodeRaw(',')); + node.add((new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression()); + } else { + node.add(new ParserNode.ParserNodeRaw(', undefined')); + } + if(expressionTokenReader.checkAndMoveNext([ + 'only' + ])) { + node.add(new ParserNode.ParserNodeRaw(', true')); + } else { + node.add(new ParserNode.ParserNodeRaw(', false')); + } + node.add(new ParserNode.ParserNodeRaw(', ' + JSON.stringify(tokenParserContext.common.serialize()))); + checkNoMoreTokens(expressionTokenReader); + node.add(new ParserNode.ParserNodeRaw(');')); + return node; + }; + DefaultTags.endraw = _flowexception; + DefaultTags.endverbatim = _flowexception; + DefaultTags.raw = function raw(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + checkNoMoreTokens(expressionTokenReader); + var offsetStart = templateTokenReader.getOffset(); + var offsetEnd = offsetStart; + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'endverbatim': function (e) { + offsetEnd = templateTokenReader.getOffset() - 1; + return true; + }, + 'endraw': function (e) { + offsetEnd = templateTokenReader.getOffset() - 1; + return true; + } + }, function (node) { + }); + var rawText = templateTokenReader.getSlice(offsetStart, offsetEnd).map(function (item) { + return (item).rawText; + }).join(''); + return new ParserNode.ParserNodeOutputText(rawText); + }; + DefaultTags.verbatim = DefaultTags.raw; + DefaultTags.endsandbox = _flowexception; + DefaultTags.sandbox = function sandbox(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + checkNoMoreTokens(expressionTokenReader); + var innerNode = new ParserNode.ParserNodeContainer(); + tokenParserContext.common.setSandbox(function () { + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'endsandbox': function (e) { + return true; + } + }, function (node) { + innerNode.add(node); + }); + }); + return new ParserNode.ParserNodeContainer([ + new ParserNode.ParserNodeRaw('runtimeContext.createScope(function() { '), + new ParserNode.ParserNodeRaw(' runtimeContext.scopeSet("__sandboxed", true);'), + innerNode, + new ParserNode.ParserNodeRaw('});') + ]); + }; + DefaultTags.endspaceless = _flowexception; + DefaultTags.spaceless = function spaceless(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + checkNoMoreTokens(expressionTokenReader); + var innerNode = new ParserNode.ParserNodeContainer(); + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'endspaceless': function (e) { + return true; + } + }, function (node) { + innerNode.add(node); + }); + return new ParserNode.ParserNodeStatementExpression(new ParserNode.ParserNodeOutputNodeExpression(new ParserNode.ParserNodeContainer([ + new ParserNode.ParserNodeRaw('runtimeContext.filter("spaceless", [runtimeContext.captureOutput(function() { '), + innerNode, + new ParserNode.ParserNodeRaw('})])') + ]))); + }; + DefaultTags.$else = _flowexception; + DefaultTags.$elseif = _flowexception; + DefaultTags.$endif = _flowexception; + DefaultTags.$if = function $if(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var didElse = false; + var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); + checkNoMoreTokens(expressionTokenReader); + var parserNodeIf = new ParserNodeIf(); + parserNodeIf.addCaseCondition(expressionNode); + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'elseif': function (e) { + if(didElse) { + throw (new Error("Can't put 'elseif' after the 'else'")); + } + var expressionNode = (new ExpressionParser.ExpressionParser(e.expressionTokenReader, tokenParserContext)).parseExpression(); + checkNoMoreTokens(expressionTokenReader); + parserNodeIf.addCaseCondition(expressionNode); + }, + 'else': function (e) { + if(didElse) { + throw (new Error("Can't have two 'else'")); + } + parserNodeIf.addElseCondition(); + didElse = true; + }, + 'endif': function (e) { + return true; + } + }, function (node) { + parserNodeIf.addCodeToCondition(node); + }); + return parserNodeIf; + }; + DefaultTags.endblock = _flowexception; + DefaultTags.block = function block(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var blockName = 'block_' + expressionTokenReader.read().value; + var innerNode = new ParserNode.ParserNodeContainer(); + if(expressionTokenReader.hasMore()) { + var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); + checkNoMoreTokens(expressionTokenReader); + innerNode.add(new ParserNode.ParserNodeReturnStatement(new ParserNode.ParserNodeWriteExpression(expressionNode))); + } else { + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'endblock': function (e) { + return true; + } + }, function (node) { + innerNode.add(node); + }); + } + tokenParserContext.setBlock(blockName, innerNode); + return new ParserNode.ParserNodeRaw('runtimeContext.putBlock(' + JSON.stringify(blockName) + ');', false); + }; + DefaultTags.$extends = function $extends(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); + checkNoMoreTokens(expressionTokenReader); + tokenParserContext.addAfterMainNode(new ParserNode.ParserNodeContainer([ + new ParserNode.ParserNodeRaw('return runtimeContext.extends('), + expressionNode, + new ParserNode.ParserNodeRaw(');') + ])); + return new ParserNode.ParserNodeRaw(''); + }; + DefaultTags.$endfor = _flowexception; + DefaultTags.$for = function $for(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + var didElse = false; + var expressionParser = new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext); + var valueId = expressionParser.parseIdentifier(); + var keyId = undefined; + var condId = undefined; + var res = expressionTokenReader.expectAndMoveNext([ + ',', + 'in' + ]); + if(res == ',') { + keyId = valueId; + valueId = expressionParser.parseIdentifier(); + expressionTokenReader.expectAndMoveNext([ + 'in' + ]); + } + var nodeList = expressionParser.parseExpression(); + if(expressionTokenReader.checkAndMoveNext([ + 'if' + ])) { + condId = expressionParser.parseExpression(); + } + checkNoMoreTokens(expressionTokenReader); + var forCode = new ParserNode.ParserNodeContainer(); + var elseCode = new ParserNode.ParserNodeContainer(); + handleOpenedTag(blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader, { + 'else': function (e) { + if(didElse) { + throw (new Error("Can't have two 'else'")); + } + didElse = true; + }, + 'endfor': function (e) { + return true; + } + }, function (node) { + if(!didElse) { + forCode.add(node); + } else { + elseCode.add(node); + } + }); + return new ParserNodeFor(keyId, condId, valueId, nodeList, forCode, elseCode); + }; + return DefaultTags; +})(); +exports.DefaultTags = DefaultTags; diff --git a/lib/lang/DefaultTags.ts b/lib/lang/DefaultTags.ts index 6fbf697..bf11012 100644 --- a/lib/lang/DefaultTags.ts +++ b/lib/lang/DefaultTags.ts @@ -45,10 +45,10 @@ export class ParserNodeAutoescape extends ParserNode.ParserNodeStatement { super(); } - generateCode() { + generateCode(context: ParserNode.ParserNodeGenerateCodeContext) { return ( - 'runtimeContext.autoescape(' + this.expression.generateCode() + ', function() {' + - this.inner.generateCode() + + 'runtimeContext.autoescape(' + this.expression.generateCode(context) + ', function() {' + + this.inner.generateCode(context) + '}, true);' ); } @@ -68,7 +68,7 @@ export class ParserNodeExpressionFilter extends ParserNode.ParserNodeExpression }); } - generateCode() { + generateCode(context: ParserNode.ParserNodeGenerateCodeContext) { var out = ''; this.filters.reverse().forEach((filter) => { @@ -76,13 +76,13 @@ export class ParserNodeExpressionFilter extends ParserNode.ParserNodeExpression }); out += 'runtimeContext.captureOutput(function () {' - out += this.inner.generateCode(); + out += this.inner.generateCode(context); out += '})'; this.filters.reverse().forEach((filter) => { if (filter.parameters && filter.parameters.expressions.length > 0) { out += ','; - out += filter.parameters.generateCode(); + out += filter.parameters.generateCode(context); } out += '])'; }); @@ -96,8 +96,8 @@ export class ParserNodeScopeSet extends ParserNode.ParserNodeStatement { super(); } - generateCode() { - return 'runtimeContext.scope.set(' + JSON.stringify(this.key) + ', ' + this.value.generateCode() + ');'; + generateCode(context: ParserNode.ParserNodeGenerateCodeContext) { + return 'runtimeContext.scope.set(' + JSON.stringify(this.key) + ', ' + this.value.generateCode(context) + ');'; } } @@ -122,15 +122,15 @@ export class ParserNodeIf extends ParserNode.ParserNodeStatement { this.conditions[this.conditions.length - 1].code.add(node); } - generateCode() { + generateCode(context: ParserNode.ParserNodeGenerateCodeContext) { var out = ''; for (var n = 0; n < this.conditions.length; n++) { var condition = this.conditions[n]; if (out != '') out += 'else '; - if (condition.expression != null) out += 'if (' + condition.expression.generateCode() + ')'; + if (condition.expression != null) out += 'if (' + condition.expression.generateCode(context) + ')'; out += '{ '; - out += condition.code.generateCode(); + out += condition.code.generateCode(context); out += '}'; } @@ -143,15 +143,15 @@ export class ParserNodeFor extends ParserNode.ParserNodeStatement { super(); } - generateCode() { + generateCode(context: ParserNode.ParserNodeGenerateCodeContext) { var out = ''; out += ('runtimeContext.createScope((function() { '); - out += (' var list = ' + this.nodeList.generateCode() + ';'); + out += (' var list = ' + this.nodeList.generateCode(context) + ';'); out += (' if (!runtimeContext.emptyList(list)) {'); out += (' runtimeContext.each(list, function(k, v) { '); - out += (' ' + (new ParserNode.ParserNodeAssignment(this.valueId, new ParserNode.ParserNodeRaw("v"))).generateCode() + ';'); + out += (' ' + (new ParserNode.ParserNodeAssignment(this.valueId, new ParserNode.ParserNodeRaw("v"))).generateCode(context) + ';'); if (this.keyId !== undefined) { - out += (' ' + (new ParserNode.ParserNodeAssignment(this.keyId, new ParserNode.ParserNodeRaw("k"))).generateCode() + ';'); + out += (' ' + (new ParserNode.ParserNodeAssignment(this.keyId, new ParserNode.ParserNodeRaw("k"))).generateCode(context) + ';'); } if (this.condId) { out += (' if (' + this.condId.generateCode() + ') { '); @@ -159,14 +159,14 @@ export class ParserNodeFor extends ParserNode.ParserNodeStatement { out += (' if (true) { '); } - out += this.forCode.generateCode(); + out += this.forCode.generateCode(context); out += ('}'); // if condition out += (' });'); // each out += ('} else {'); { - out += this.elseCode.generateCode(); + out += this.elseCode.generateCode(context); } out += ('} '); // if/else out += ('}));'); // createScope @@ -366,7 +366,14 @@ export class DefaultTags { checkNoMoreTokens(expressionTokenReader); return new ParserNode.ParserNodeStatementExpression( - new ParserNode.ParserNodeAssignment(aliasNode, new ParserNode.ParserNodeRaw('runtimeContext.import(' + fileNameNode.generateCode() + ')')) + new ParserNode.ParserNodeAssignment( + aliasNode, + new ParserNode.ParserNodeContainerExpression([ + new ParserNode.ParserNodeRaw('runtimeContext.import('), + fileNameNode, + new ParserNode.ParserNodeRaw(')'), + ]) + ) ); } @@ -573,7 +580,7 @@ export class DefaultTags { tokenParserContext.setBlock(blockName, innerNode); - return new ParserNode.ParserNodeRaw('runtimeContext.putBlock(' + JSON.stringify(blockName) + ');'); + return new ParserNode.ParserNodeRaw('runtimeContext.putBlock(' + JSON.stringify(blockName) + ');', false); } // EXTENDS @@ -581,11 +588,13 @@ export class DefaultTags { var expressionNode = (new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression(); checkNoMoreTokens(expressionTokenReader); - return new ParserNode.ParserNodeContainer([ + tokenParserContext.addAfterMainNode(new ParserNode.ParserNodeContainer([ new ParserNode.ParserNodeRaw('return runtimeContext.extends('), expressionNode, new ParserNode.ParserNodeRaw(');') - ]); + ])); + + return new ParserNode.ParserNodeRaw(''); } // http://twig.sensiolabs.org/doc/tags/for.html diff --git a/lib/parser/ParserNode.js b/lib/parser/ParserNode.js index 7fcde6c..70c4003 100644 --- a/lib/parser/ParserNode.js +++ b/lib/parser/ParserNode.js @@ -1,413 +1,451 @@ -var __extends = this.__extends || function (d, b) { - function __() { this.constructor = d; } - __.prototype = b.prototype; - d.prototype = new __(); -}; -var ParserNode = (function () { - function ParserNode() { - this.type = '-'; - } - ParserNode.prototype.generateCode = function () { - return ''; - }; - ParserNode.prototype.optimize = function () { - return this; - }; - return ParserNode; -})(); -exports.ParserNode = ParserNode; -var ParserNodeExpression = (function (_super) { - __extends(ParserNodeExpression, _super); - function ParserNodeExpression() { - _super.apply(this, arguments); - - } - return ParserNodeExpression; -})(ParserNode); -exports.ParserNodeExpression = ParserNodeExpression; -var ParserNodeWriteExpression = (function (_super) { - __extends(ParserNodeWriteExpression, _super); - function ParserNodeWriteExpression(expression) { - _super.call(this); - this.expression = expression; - } - ParserNodeWriteExpression.prototype.generateCode = function () { - return 'runtimeContext.writeExpression(' + this.expression.generateCode() + ')'; - }; - return ParserNodeWriteExpression; -})(ParserNodeExpression); -exports.ParserNodeWriteExpression = ParserNodeWriteExpression; -var ParserNodeContainer = (function (_super) { - __extends(ParserNodeContainer, _super); - function ParserNodeContainer(nodes) { - if (typeof nodes === "undefined") { nodes = null; } - _super.call(this); - this.nodes = nodes; - this.type = 'ParserNodeContainer'; - if(this.nodes == null) { - this.nodes = []; - } - } - ParserNodeContainer.prototype.add = function (node) { - this.nodes.push(node); - }; - ParserNodeContainer.prototype.generateCode = function () { - var output = ''; - for(var n in this.nodes) { - output += this.nodes[n].generateCode(); - } - return output; - }; - return ParserNodeContainer; -})(ParserNode); -exports.ParserNodeContainer = ParserNodeContainer; -var ParserNodeObjectItem = (function (_super) { - __extends(ParserNodeObjectItem, _super); - function ParserNodeObjectItem(key, value) { - _super.call(this); - this.key = key; - this.value = value; - this.type = 'ParserNodeObjectItem'; - } - ParserNodeObjectItem.prototype.generateCode = function () { - return this.key.generateCode() + ' : ' + this.value.generateCode(); - }; - return ParserNodeObjectItem; -})(ParserNode); -exports.ParserNodeObjectItem = ParserNodeObjectItem; -var ParserNodeObjectContainer = (function (_super) { - __extends(ParserNodeObjectContainer, _super); - function ParserNodeObjectContainer(items) { - _super.call(this); - this.items = items; - this.type = 'ParserNodeObjectContainer'; - } - ParserNodeObjectContainer.prototype.generateCode = function () { - return '{' + this.items.map(function (node) { - return node.generateCode(); - }).join(', ') + '}'; - }; - return ParserNodeObjectContainer; -})(ParserNodeExpression); -exports.ParserNodeObjectContainer = ParserNodeObjectContainer; -var ParserNodeArrayContainer = (function (_super) { - __extends(ParserNodeArrayContainer, _super); - function ParserNodeArrayContainer(items) { - _super.call(this); - this.items = items; - this.type = 'ParserNodeArrayContainer'; - } - ParserNodeArrayContainer.prototype.generateCode = function () { - return '[' + this.items.map(function (node) { - return node.generateCode(); - }).join(', ') + ']'; - }; - return ParserNodeArrayContainer; -})(ParserNodeExpression); -exports.ParserNodeArrayContainer = ParserNodeArrayContainer; -var ParserNodeLiteral = (function (_super) { - __extends(ParserNodeLiteral, _super); - function ParserNodeLiteral(value) { - _super.call(this); - this.value = value; - this.type = 'ParserNodeLiteral'; - } - ParserNodeLiteral.prototype.generateCode = function () { - return JSON.stringify(this.value); - }; - return ParserNodeLiteral; -})(ParserNodeExpression); -exports.ParserNodeLiteral = ParserNodeLiteral; -var ParserNodeLeftValue = (function (_super) { - __extends(ParserNodeLeftValue, _super); - function ParserNodeLeftValue() { - _super.apply(this, arguments); - - this.type = 'ParserNodeLeftValue'; - } - ParserNodeLeftValue.prototype.generateAssign = function (expr) { - throw (new Error("Must implement")); - }; - return ParserNodeLeftValue; -})(ParserNodeExpression); -exports.ParserNodeLeftValue = ParserNodeLeftValue; -var ParserNodeIdentifier = (function (_super) { - __extends(ParserNodeIdentifier, _super); - function ParserNodeIdentifier(value) { - _super.call(this); - this.value = value; - this.type = 'ParserNodeIdentifier'; - } - ParserNodeIdentifier.prototype.generateAssign = function (expr) { - return 'runtimeContext.scopeSet(' + JSON.stringify(this.value) + ', ' + expr.generateCode() + ')'; - }; - ParserNodeIdentifier.prototype.generateCode = function () { - return 'runtimeContext.scopeGet(' + JSON.stringify(this.value) + ')'; - }; - return ParserNodeIdentifier; -})(ParserNodeLeftValue); -exports.ParserNodeIdentifier = ParserNodeIdentifier; -var ParserNodeStatement = (function (_super) { - __extends(ParserNodeStatement, _super); - function ParserNodeStatement() { - _super.apply(this, arguments); - - this.type = 'ParserNodeStatement'; - } - return ParserNodeStatement; -})(ParserNode); -exports.ParserNodeStatement = ParserNodeStatement; -var ParserNodeRaw = (function (_super) { - __extends(ParserNodeRaw, _super); - function ParserNodeRaw(value) { - _super.call(this); - this.value = value; - this.type = 'ParserNodeRaw'; - } - ParserNodeRaw.prototype.generateCode = function () { - return this.value; - }; - return ParserNodeRaw; -})(ParserNodeExpression); -exports.ParserNodeRaw = ParserNodeRaw; -var ParserNodeStatementExpression = (function (_super) { - __extends(ParserNodeStatementExpression, _super); - function ParserNodeStatementExpression(expression) { - _super.call(this); - this.expression = expression; - this.type = 'ParserNodeStatementExpression'; - } - ParserNodeStatementExpression.prototype.generateCode = function () { - return this.expression.generateCode() + ';'; - }; - return ParserNodeStatementExpression; -})(ParserNodeStatement); -exports.ParserNodeStatementExpression = ParserNodeStatementExpression; -var ParserNodeAssignment = (function (_super) { - __extends(ParserNodeAssignment, _super); - function ParserNodeAssignment(leftValue, rightValue) { - _super.call(this); - this.leftValue = leftValue; - this.rightValue = rightValue; - this.type = 'ParserNodeAssignment'; - } - ParserNodeAssignment.prototype.generateCode = function () { - return this.leftValue.generateAssign(this.rightValue); - }; - return ParserNodeAssignment; -})(ParserNodeExpression); -exports.ParserNodeAssignment = ParserNodeAssignment; -var ParserNodeCommaExpression = (function (_super) { - __extends(ParserNodeCommaExpression, _super); - function ParserNodeCommaExpression(expressions, names) { - if (typeof names === "undefined") { names = null; } - _super.call(this); - this.expressions = expressions; - this.names = names; - this.type = 'ParserNodeCommaExpression'; - } - ParserNodeCommaExpression.prototype.generateCode = function () { - return this.expressions.map(function (item) { - return item.generateCode(); - }).join(', '); - }; - return ParserNodeCommaExpression; -})(ParserNode); -exports.ParserNodeCommaExpression = ParserNodeCommaExpression; -var ParserNodeArrayAccess = (function (_super) { - __extends(ParserNodeArrayAccess, _super); - function ParserNodeArrayAccess(object, key) { - _super.call(this); - this.object = object; - this.key = key; - this.type = 'ParserNodeArrayAccess'; - } - ParserNodeArrayAccess.prototype.generateCode = function () { - return 'runtimeContext.access(' + this.object.generateCode() + ', ' + this.key.generateCode() + ')'; - }; - return ParserNodeArrayAccess; -})(ParserNodeExpression); -exports.ParserNodeArrayAccess = ParserNodeArrayAccess; -var ParserNodeArraySlice = (function (_super) { - __extends(ParserNodeArraySlice, _super); - function ParserNodeArraySlice(object, left, right) { - _super.call(this); - this.object = object; - this.left = left; - this.right = right; - this.type = 'ParserNodeArraySlice'; - } - ParserNodeArraySlice.prototype.generateCode = function () { - return 'runtimeContext.slice(' + this.object.generateCode() + ', ' + this.left.generateCode() + ', ' + this.right.generateCode() + ')'; - }; - return ParserNodeArraySlice; -})(ParserNodeExpression); -exports.ParserNodeArraySlice = ParserNodeArraySlice; -var ParserNodeFunctionCall = (function (_super) { - __extends(ParserNodeFunctionCall, _super); - function ParserNodeFunctionCall(functionExpr, arguments) { - _super.call(this); - this.functionExpr = functionExpr; - this.arguments = arguments; - this.type = 'ParserNodeFunctionCall'; - } - ParserNodeFunctionCall.prototype.generateCode = function () { - if(this.functionExpr instanceof ParserNodeArrayAccess) { - var arrayAccess = this.functionExpr; - return 'runtimeContext.callContext(' + arrayAccess.object.generateCode() + ', ' + arrayAccess.key.generateCode() + ', [' + this.arguments.generateCode() + '], ' + JSON.stringify(this.arguments.names) + ')'; - } else { - return 'runtimeContext.call(' + this.functionExpr.generateCode() + ', [' + this.arguments.generateCode() + '], ' + JSON.stringify(this.arguments.names) + ')'; - } - }; - return ParserNodeFunctionCall; -})(ParserNodeExpression); -exports.ParserNodeFunctionCall = ParserNodeFunctionCall; -var ParserNodeFilterCall = (function (_super) { - __extends(ParserNodeFilterCall, _super); - function ParserNodeFilterCall(filterName, arguments) { - _super.call(this); - this.filterName = filterName; - this.arguments = arguments; - this.type = 'ParserNodeFilterCall'; - } - ParserNodeFilterCall.prototype.generateCode = function () { - return 'runtimeContext.filter(' + JSON.stringify(this.filterName) + ', [' + this.arguments.generateCode() + '])'; - }; - return ParserNodeFilterCall; -})(ParserNodeExpression); -exports.ParserNodeFilterCall = ParserNodeFilterCall; -var ParserNodeUnaryOperation = (function (_super) { - __extends(ParserNodeUnaryOperation, _super); - function ParserNodeUnaryOperation(operator, right) { - _super.call(this); - this.operator = operator; - this.right = right; - this.type = 'ParserNodeUnaryOperation'; - } - ParserNodeUnaryOperation.prototype.generateCode = function () { - switch(this.operator) { - case 'not': - return '!(' + this.right.generateCode() + ')'; - default: - return this.operator + '(' + this.right.generateCode() + ')'; - } - }; - return ParserNodeUnaryOperation; -})(ParserNodeExpression); -exports.ParserNodeUnaryOperation = ParserNodeUnaryOperation; -var ParserNodeBinaryOperation = (function (_super) { - __extends(ParserNodeBinaryOperation, _super); - function ParserNodeBinaryOperation(operator, left, right) { - _super.call(this); - this.operator = operator; - this.left = left; - this.right = right; - this.type = 'ParserNodeBinaryOperation'; - } - ParserNodeBinaryOperation.prototype.generateCode = function () { - switch(this.operator) { - case 'b-or': - return '("" + ' + this.left.generateCode() + ' | ' + this.right.generateCode() + ')'; - case 'b-and': - return '("" + ' + this.left.generateCode() + ' & ' + this.right.generateCode() + ')'; - case 'b-xor': - return '("" + ' + this.left.generateCode() + ' ^ ' + this.right.generateCode() + ')'; - case '~': - return '("" + ' + this.left.generateCode() + ' + ' + this.right.generateCode() + ')'; - case '..': - return 'runtimeContext.range(' + this.left.generateCode() + ', ' + this.right.generateCode() + ')'; - case '?:': - return 'runtimeContext.ternaryShortcut(' + this.left.generateCode() + ', ' + this.right.generateCode() + ')'; - case '//': - return 'Math.floor(' + this.left.generateCode() + ' / ' + this.right.generateCode() + ')'; - case '**': - return 'Math.pow(' + this.left.generateCode() + ',' + this.right.generateCode() + ')'; - case 'not in': - case 'in': - var ret = 'runtimeContext.inArray(' + this.left.generateCode() + ',' + this.right.generateCode() + ')'; - if((this.operator == 'not in')) { - ret = '!(' + ret + ')'; - } - return ret; - case 'is': - case 'is not': - var ret = ''; - var left = this.left; - var right = this.right; - if(this.right instanceof ParserNodeUnaryOperation) { - right = this.right.right; - } - if(right instanceof ParserNodeFunctionCall) { - ret = 'runtimeContext.test(' + (right).functionExpr.generateCode() + ', [' + left.generateCode() + ',' + (right).arguments.generateCode() + '])'; - } else if(right instanceof ParserNodeIdentifier) { - ret = 'runtimeContext.test(' + JSON.stringify((right).value) + ', [' + left.generateCode() + '])'; - } else if(right instanceof ParserNodeLiteral && (right).value === null) { - ret = 'runtimeContext.test("null", [' + left.generateCode() + '])'; - } else { - throw (new Error("ParserNodeBinaryOperation: Not implemented 'is' operator for tests with " + JSON.stringify(right))); - } - if(this.operator == 'is not') { - ret = '!(' + ret + ')'; - } - return ret; - default: - return ('(' + this.left.generateCode() + ' ' + this.operator + ' ' + this.right.generateCode() + ')'); - } - }; - return ParserNodeBinaryOperation; -})(ParserNodeExpression); -exports.ParserNodeBinaryOperation = ParserNodeBinaryOperation; -var ParserNodeTernaryOperation = (function (_super) { - __extends(ParserNodeTernaryOperation, _super); - function ParserNodeTernaryOperation(cond, exprTrue, exprFalse) { - _super.call(this); - this.cond = cond; - this.exprTrue = exprTrue; - this.exprFalse = exprFalse; - this.type = 'ParserNodeTernaryOperation'; - } - ParserNodeTernaryOperation.prototype.generateCode = function () { - return ('(' + this.cond.generateCode() + " ? " + this.exprTrue.generateCode() + " : " + this.exprFalse.generateCode() + ')'); - }; - return ParserNodeTernaryOperation; -})(ParserNode); -exports.ParserNodeTernaryOperation = ParserNodeTernaryOperation; -var ParserNodeOutputText = (function (_super) { - __extends(ParserNodeOutputText, _super); - function ParserNodeOutputText(text) { - _super.call(this); - this.text = text; - this.type = 'ParserNodeOutputText'; - } - ParserNodeOutputText.prototype.generateCode = function () { - return 'runtimeContext.write(' + JSON.stringify(this.text) + ');'; - }; - return ParserNodeOutputText; -})(ParserNode); -exports.ParserNodeOutputText = ParserNodeOutputText; -var ParserNodeOutputNodeExpression = (function (_super) { - __extends(ParserNodeOutputNodeExpression, _super); - function ParserNodeOutputNodeExpression(expression) { - _super.call(this); - this.expression = expression; - this.type = 'ParserNodeOutputNodeExpression'; - } - ParserNodeOutputNodeExpression.prototype.generateCode = function () { - return 'runtimeContext.write(' + this.expression.generateCode() + ')'; - }; - return ParserNodeOutputNodeExpression; -})(ParserNodeExpression); -exports.ParserNodeOutputNodeExpression = ParserNodeOutputNodeExpression; -var ParserNodeReturnStatement = (function (_super) { - __extends(ParserNodeReturnStatement, _super); - function ParserNodeReturnStatement(expression) { - _super.call(this); - this.expression = expression; - this.type = 'ParserNodeReturnStatement'; - } - ParserNodeReturnStatement.prototype.generateCode = function () { - return 'return ' + this.expression.generateCode() + ';'; - }; - return ParserNodeReturnStatement; -})(ParserNodeStatement); -exports.ParserNodeReturnStatement = ParserNodeReturnStatement; +var __extends = this.__extends || function (d, b) { + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var ParserNode = (function () { + function ParserNode() { + this.type = '-'; + } + ParserNode.prototype.generateCode = function (context) { + return ''; + }; + ParserNode.prototype.optimize = function () { + return this; + }; + return ParserNode; +})(); +exports.ParserNode = ParserNode; +var ParserNodeExpression = (function (_super) { + __extends(ParserNodeExpression, _super); + function ParserNodeExpression() { + _super.apply(this, arguments); + + } + return ParserNodeExpression; +})(ParserNode); +exports.ParserNodeExpression = ParserNodeExpression; +var ParserNodeWriteExpression = (function (_super) { + __extends(ParserNodeWriteExpression, _super); + function ParserNodeWriteExpression(expression) { + _super.call(this); + this.expression = expression; + } + ParserNodeWriteExpression.prototype.generateCode = function (context) { + if(!context.doWrite) { + return ''; + } + return 'runtimeContext.writeExpression(' + this.expression.generateCode(context) + ')'; + }; + return ParserNodeWriteExpression; +})(ParserNodeExpression); +exports.ParserNodeWriteExpression = ParserNodeWriteExpression; +var ParserNodeContainer = (function (_super) { + __extends(ParserNodeContainer, _super); + function ParserNodeContainer(nodes) { + if (typeof nodes === "undefined") { nodes = null; } + _super.call(this); + this.nodes = nodes; + this.type = 'ParserNodeContainer'; + if(this.nodes == null) { + this.nodes = []; + } + } + ParserNodeContainer.prototype.add = function (node) { + this.nodes.push(node); + }; + ParserNodeContainer.prototype.generateCode = function (context) { + var output = ''; + for(var n in this.nodes) { + output += this.nodes[n].generateCode(context); + } + return output; + }; + return ParserNodeContainer; +})(ParserNode); +exports.ParserNodeContainer = ParserNodeContainer; +var ParserNodeContainerExpression = (function (_super) { + __extends(ParserNodeContainerExpression, _super); + function ParserNodeContainerExpression(nodes) { + if (typeof nodes === "undefined") { nodes = null; } + _super.call(this); + this.nodes = nodes; + this.type = 'ParserNodeContainerExpression'; + if(this.nodes == null) { + this.nodes = []; + } + } + ParserNodeContainerExpression.prototype.add = function (node) { + this.nodes.push(node); + }; + ParserNodeContainerExpression.prototype.generateCode = function (context) { + var output = ''; + for(var n in this.nodes) { + output += this.nodes[n].generateCode(context); + } + return output; + }; + return ParserNodeContainerExpression; +})(ParserNodeExpression); +exports.ParserNodeContainerExpression = ParserNodeContainerExpression; +var ParserNodeObjectItem = (function (_super) { + __extends(ParserNodeObjectItem, _super); + function ParserNodeObjectItem(key, value) { + _super.call(this); + this.key = key; + this.value = value; + this.type = 'ParserNodeObjectItem'; + } + ParserNodeObjectItem.prototype.generateCode = function (context) { + return this.key.generateCode(context) + ' : ' + this.value.generateCode(context); + }; + return ParserNodeObjectItem; +})(ParserNode); +exports.ParserNodeObjectItem = ParserNodeObjectItem; +var ParserNodeObjectContainer = (function (_super) { + __extends(ParserNodeObjectContainer, _super); + function ParserNodeObjectContainer(items) { + _super.call(this); + this.items = items; + this.type = 'ParserNodeObjectContainer'; + } + ParserNodeObjectContainer.prototype.generateCode = function (context) { + return '{' + this.items.map(function (node) { + return node.generateCode(context); + }).join(', ') + '}'; + }; + return ParserNodeObjectContainer; +})(ParserNodeExpression); +exports.ParserNodeObjectContainer = ParserNodeObjectContainer; +var ParserNodeArrayContainer = (function (_super) { + __extends(ParserNodeArrayContainer, _super); + function ParserNodeArrayContainer(items) { + _super.call(this); + this.items = items; + this.type = 'ParserNodeArrayContainer'; + } + ParserNodeArrayContainer.prototype.generateCode = function (context) { + return '[' + this.items.map(function (node) { + return node.generateCode(context); + }).join(', ') + ']'; + }; + return ParserNodeArrayContainer; +})(ParserNodeExpression); +exports.ParserNodeArrayContainer = ParserNodeArrayContainer; +var ParserNodeLiteral = (function (_super) { + __extends(ParserNodeLiteral, _super); + function ParserNodeLiteral(value) { + _super.call(this); + this.value = value; + this.type = 'ParserNodeLiteral'; + } + ParserNodeLiteral.prototype.generateCode = function (context) { + return JSON.stringify(this.value); + }; + return ParserNodeLiteral; +})(ParserNodeExpression); +exports.ParserNodeLiteral = ParserNodeLiteral; +var ParserNodeLeftValue = (function (_super) { + __extends(ParserNodeLeftValue, _super); + function ParserNodeLeftValue() { + _super.apply(this, arguments); + + this.type = 'ParserNodeLeftValue'; + } + ParserNodeLeftValue.prototype.generateAssign = function (context, expr) { + throw (new Error("Must implement")); + }; + return ParserNodeLeftValue; +})(ParserNodeExpression); +exports.ParserNodeLeftValue = ParserNodeLeftValue; +var ParserNodeIdentifier = (function (_super) { + __extends(ParserNodeIdentifier, _super); + function ParserNodeIdentifier(value) { + _super.call(this); + this.value = value; + this.type = 'ParserNodeIdentifier'; + } + ParserNodeIdentifier.prototype.generateAssign = function (context, expr) { + return 'runtimeContext.scopeSet(' + JSON.stringify(this.value) + ', ' + expr.generateCode(context) + ')'; + }; + ParserNodeIdentifier.prototype.generateCode = function (context) { + return 'runtimeContext.scopeGet(' + JSON.stringify(this.value) + ')'; + }; + return ParserNodeIdentifier; +})(ParserNodeLeftValue); +exports.ParserNodeIdentifier = ParserNodeIdentifier; +var ParserNodeStatement = (function (_super) { + __extends(ParserNodeStatement, _super); + function ParserNodeStatement() { + _super.apply(this, arguments); + + this.type = 'ParserNodeStatement'; + } + return ParserNodeStatement; +})(ParserNode); +exports.ParserNodeStatement = ParserNodeStatement; +var ParserNodeRaw = (function (_super) { + __extends(ParserNodeRaw, _super); + function ParserNodeRaw(value, putAlways) { + if (typeof putAlways === "undefined") { putAlways = true; } + _super.call(this); + this.value = value; + this.putAlways = putAlways; + this.type = 'ParserNodeRaw'; + } + ParserNodeRaw.prototype.generateCode = function (context) { + if(!context.doWrite && !this.putAlways) { + return ''; + } + return this.value; + }; + return ParserNodeRaw; +})(ParserNodeExpression); +exports.ParserNodeRaw = ParserNodeRaw; +var ParserNodeStatementExpression = (function (_super) { + __extends(ParserNodeStatementExpression, _super); + function ParserNodeStatementExpression(expression) { + _super.call(this); + this.expression = expression; + this.type = 'ParserNodeStatementExpression'; + } + ParserNodeStatementExpression.prototype.generateCode = function (context) { + return this.expression.generateCode(context) + ';'; + }; + return ParserNodeStatementExpression; +})(ParserNodeStatement); +exports.ParserNodeStatementExpression = ParserNodeStatementExpression; +var ParserNodeAssignment = (function (_super) { + __extends(ParserNodeAssignment, _super); + function ParserNodeAssignment(leftValue, rightValue) { + _super.call(this); + this.leftValue = leftValue; + this.rightValue = rightValue; + this.type = 'ParserNodeAssignment'; + } + ParserNodeAssignment.prototype.generateCode = function (context) { + return this.leftValue.generateAssign(context, this.rightValue); + }; + return ParserNodeAssignment; +})(ParserNodeExpression); +exports.ParserNodeAssignment = ParserNodeAssignment; +var ParserNodeCommaExpression = (function (_super) { + __extends(ParserNodeCommaExpression, _super); + function ParserNodeCommaExpression(expressions, names) { + if (typeof names === "undefined") { names = null; } + _super.call(this); + this.expressions = expressions; + this.names = names; + this.type = 'ParserNodeCommaExpression'; + } + ParserNodeCommaExpression.prototype.generateCode = function (context) { + return this.expressions.map(function (item) { + return item.generateCode(context); + }).join(', '); + }; + return ParserNodeCommaExpression; +})(ParserNode); +exports.ParserNodeCommaExpression = ParserNodeCommaExpression; +var ParserNodeArrayAccess = (function (_super) { + __extends(ParserNodeArrayAccess, _super); + function ParserNodeArrayAccess(object, key) { + _super.call(this); + this.object = object; + this.key = key; + this.type = 'ParserNodeArrayAccess'; + } + ParserNodeArrayAccess.prototype.generateCode = function (context) { + return 'runtimeContext.access(' + this.object.generateCode(context) + ', ' + this.key.generateCode(context) + ')'; + }; + return ParserNodeArrayAccess; +})(ParserNodeExpression); +exports.ParserNodeArrayAccess = ParserNodeArrayAccess; +var ParserNodeArraySlice = (function (_super) { + __extends(ParserNodeArraySlice, _super); + function ParserNodeArraySlice(object, left, right) { + _super.call(this); + this.object = object; + this.left = left; + this.right = right; + this.type = 'ParserNodeArraySlice'; + } + ParserNodeArraySlice.prototype.generateCode = function (context) { + return 'runtimeContext.slice(' + this.object.generateCode(context) + ', ' + this.left.generateCode(context) + ', ' + this.right.generateCode(context) + ')'; + }; + return ParserNodeArraySlice; +})(ParserNodeExpression); +exports.ParserNodeArraySlice = ParserNodeArraySlice; +var ParserNodeFunctionCall = (function (_super) { + __extends(ParserNodeFunctionCall, _super); + function ParserNodeFunctionCall(functionExpr, arguments) { + _super.call(this); + this.functionExpr = functionExpr; + this.arguments = arguments; + this.type = 'ParserNodeFunctionCall'; + } + ParserNodeFunctionCall.prototype.generateCode = function (context) { + if(this.functionExpr instanceof ParserNodeArrayAccess) { + var arrayAccess = this.functionExpr; + return 'runtimeContext.callContext(' + arrayAccess.object.generateCode(context) + ', ' + arrayAccess.key.generateCode(context) + ', [' + this.arguments.generateCode(context) + '], ' + JSON.stringify(this.arguments.names) + ')'; + } else { + return 'runtimeContext.call(' + this.functionExpr.generateCode(context) + ', [' + this.arguments.generateCode(context) + '], ' + JSON.stringify(this.arguments.names) + ')'; + } + }; + return ParserNodeFunctionCall; +})(ParserNodeExpression); +exports.ParserNodeFunctionCall = ParserNodeFunctionCall; +var ParserNodeFilterCall = (function (_super) { + __extends(ParserNodeFilterCall, _super); + function ParserNodeFilterCall(filterName, arguments) { + _super.call(this); + this.filterName = filterName; + this.arguments = arguments; + this.type = 'ParserNodeFilterCall'; + } + ParserNodeFilterCall.prototype.generateCode = function (context) { + return 'runtimeContext.filter(' + JSON.stringify(this.filterName) + ', [' + this.arguments.generateCode(context) + '])'; + }; + return ParserNodeFilterCall; +})(ParserNodeExpression); +exports.ParserNodeFilterCall = ParserNodeFilterCall; +var ParserNodeUnaryOperation = (function (_super) { + __extends(ParserNodeUnaryOperation, _super); + function ParserNodeUnaryOperation(operator, right) { + _super.call(this); + this.operator = operator; + this.right = right; + this.type = 'ParserNodeUnaryOperation'; + } + ParserNodeUnaryOperation.prototype.generateCode = function (context) { + switch(this.operator) { + case 'not': + return '!(' + this.right.generateCode(context) + ')'; + default: + return this.operator + '(' + this.right.generateCode(context) + ')'; + } + }; + return ParserNodeUnaryOperation; +})(ParserNodeExpression); +exports.ParserNodeUnaryOperation = ParserNodeUnaryOperation; +var ParserNodeBinaryOperation = (function (_super) { + __extends(ParserNodeBinaryOperation, _super); + function ParserNodeBinaryOperation(operator, left, right) { + _super.call(this); + this.operator = operator; + this.left = left; + this.right = right; + this.type = 'ParserNodeBinaryOperation'; + } + ParserNodeBinaryOperation.prototype.generateCode = function (context) { + switch(this.operator) { + case 'b-or': + return '("" + ' + this.left.generateCode() + ' | ' + this.right.generateCode() + ')'; + case 'b-and': + return '("" + ' + this.left.generateCode() + ' & ' + this.right.generateCode() + ')'; + case 'b-xor': + return '("" + ' + this.left.generateCode() + ' ^ ' + this.right.generateCode() + ')'; + case '~': + return '("" + ' + this.left.generateCode() + ' + ' + this.right.generateCode() + ')'; + case '..': + return 'runtimeContext.range(' + this.left.generateCode() + ', ' + this.right.generateCode() + ')'; + case '?:': + return 'runtimeContext.ternaryShortcut(' + this.left.generateCode() + ', ' + this.right.generateCode() + ')'; + case '//': + return 'Math.floor(' + this.left.generateCode() + ' / ' + this.right.generateCode() + ')'; + case '**': + return 'Math.pow(' + this.left.generateCode() + ',' + this.right.generateCode() + ')'; + case 'not in': + case 'in': + var ret = 'runtimeContext.inArray(' + this.left.generateCode() + ',' + this.right.generateCode() + ')'; + if((this.operator == 'not in')) { + ret = '!(' + ret + ')'; + } + return ret; + case 'is': + case 'is not': + var ret = ''; + var left = this.left; + var right = this.right; + if(this.right instanceof ParserNodeUnaryOperation) { + right = this.right.right; + } + if(right instanceof ParserNodeFunctionCall) { + ret = 'runtimeContext.test(' + (right).functionExpr.generateCode(context) + ', [' + left.generateCode(context) + ',' + (right).arguments.generateCode(context) + '])'; + } else if(right instanceof ParserNodeIdentifier) { + ret = 'runtimeContext.test(' + JSON.stringify((right).value) + ', [' + left.generateCode(context) + '])'; + } else if(right instanceof ParserNodeLiteral && (right).value === null) { + ret = 'runtimeContext.test("null", [' + left.generateCode(context) + '])'; + } else { + throw (new Error("ParserNodeBinaryOperation: Not implemented 'is' operator for tests with " + JSON.stringify(right))); + } + if(this.operator == 'is not') { + ret = '!(' + ret + ')'; + } + return ret; + default: + return ('(' + this.left.generateCode() + ' ' + this.operator + ' ' + this.right.generateCode() + ')'); + } + }; + return ParserNodeBinaryOperation; +})(ParserNodeExpression); +exports.ParserNodeBinaryOperation = ParserNodeBinaryOperation; +var ParserNodeTernaryOperation = (function (_super) { + __extends(ParserNodeTernaryOperation, _super); + function ParserNodeTernaryOperation(cond, exprTrue, exprFalse) { + _super.call(this); + this.cond = cond; + this.exprTrue = exprTrue; + this.exprFalse = exprFalse; + this.type = 'ParserNodeTernaryOperation'; + } + ParserNodeTernaryOperation.prototype.generateCode = function (context) { + return ('(' + this.cond.generateCode(context) + " ? " + this.exprTrue.generateCode(context) + " : " + this.exprFalse.generateCode(context) + ')'); + }; + return ParserNodeTernaryOperation; +})(ParserNode); +exports.ParserNodeTernaryOperation = ParserNodeTernaryOperation; +var ParserNodeOutputText = (function (_super) { + __extends(ParserNodeOutputText, _super); + function ParserNodeOutputText(text) { + _super.call(this); + this.text = text; + this.type = 'ParserNodeOutputText'; + } + ParserNodeOutputText.prototype.generateCode = function (context) { + if(!context.doWrite) { + return ''; + } + return 'runtimeContext.write(' + JSON.stringify(this.text) + ');'; + }; + return ParserNodeOutputText; +})(ParserNode); +exports.ParserNodeOutputText = ParserNodeOutputText; +var ParserNodeOutputNodeExpression = (function (_super) { + __extends(ParserNodeOutputNodeExpression, _super); + function ParserNodeOutputNodeExpression(expression) { + _super.call(this); + this.expression = expression; + this.type = 'ParserNodeOutputNodeExpression'; + } + ParserNodeOutputNodeExpression.prototype.generateCode = function (context) { + if(!context.doWrite) { + return ''; + } + return 'runtimeContext.write(' + this.expression.generateCode(context) + ')'; + }; + return ParserNodeOutputNodeExpression; +})(ParserNodeExpression); +exports.ParserNodeOutputNodeExpression = ParserNodeOutputNodeExpression; +var ParserNodeReturnStatement = (function (_super) { + __extends(ParserNodeReturnStatement, _super); + function ParserNodeReturnStatement(expression) { + _super.call(this); + this.expression = expression; + this.type = 'ParserNodeReturnStatement'; + } + ParserNodeReturnStatement.prototype.generateCode = function (context) { + return 'return ' + this.expression.generateCode(context) + ';'; + }; + return ParserNodeReturnStatement; +})(ParserNodeStatement); +exports.ParserNodeReturnStatement = ParserNodeReturnStatement; diff --git a/lib/parser/ParserNode.ts b/lib/parser/ParserNode.ts index 1fe3d0b..71a4fab 100644 --- a/lib/parser/ParserNode.ts +++ b/lib/parser/ParserNode.ts @@ -13,10 +13,14 @@ // } //} -export export class ParserNode { +export interface ParserNodeGenerateCodeContext { + doWrite: bool; +} + +export class ParserNode { type: string = '-'; - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { return ''; } @@ -33,8 +37,9 @@ export export class ParserNodeWriteExpression extends ParserNodeExpression { super(); } - generateCode() { - return 'runtimeContext.writeExpression(' + this.expression.generateCode() + ')'; + generateCode(context: ParserNodeGenerateCodeContext) { + if (!context.doWrite) return ''; + return 'runtimeContext.writeExpression(' + this.expression.generateCode(context) + ')'; } } @@ -53,15 +58,35 @@ export class ParserNodeContainer extends ParserNode { this.nodes.push(node); } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { var output = ''; for (var n in this.nodes) { - output += this.nodes[n].generateCode(); + output += this.nodes[n].generateCode(context); } return output; } } +export class ParserNodeContainerExpression extends ParserNodeExpression { + type: string = 'ParserNodeContainerExpression'; + + constructor(public nodes: ParserNode[] = null) { + super(); + if (this.nodes == null) this.nodes = []; + } + + add(node: ParserNode) { + this.nodes.push(node); + } + + generateCode(context: ParserNodeGenerateCodeContext) { + var output = ''; + for (var n in this.nodes) { + output += this.nodes[n].generateCode(context); + } + return output; + } +} /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -73,8 +98,8 @@ export class ParserNodeObjectItem extends ParserNode { super(); } - generateCode() { - return this.key.generateCode() + ' : ' + this.value.generateCode(); + generateCode(context: ParserNodeGenerateCodeContext) { + return this.key.generateCode(context) + ' : ' + this.value.generateCode(context); } } @@ -85,8 +110,8 @@ export class ParserNodeObjectContainer extends ParserNodeExpression { super(); } - generateCode() { - return '{' + this.items.map(node => node.generateCode()).join(', ') + '}'; + generateCode(context: ParserNodeGenerateCodeContext) { + return '{' + this.items.map(node => node.generateCode(context)).join(', ') + '}'; } } @@ -97,8 +122,8 @@ export class ParserNodeArrayContainer extends ParserNodeExpression { super(); } - generateCode() { - return '[' + this.items.map(node => node.generateCode()).join(', ') + ']'; + generateCode(context: ParserNodeGenerateCodeContext) { + return '[' + this.items.map(node => node.generateCode(context)).join(', ') + ']'; } } @@ -108,7 +133,7 @@ export class ParserNodeArrayContainer extends ParserNodeExpression { export interface ParseNodeLiteralIdentifier { type: string; value: any; - generateCode(); + generateCode(context: ParserNodeGenerateCodeContext); optimize(); } @@ -119,7 +144,7 @@ export class ParserNodeLiteral extends ParserNodeExpression implements ParseNode super(); } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { return JSON.stringify(this.value); } } @@ -130,7 +155,7 @@ export class ParserNodeLiteral extends ParserNodeExpression implements ParseNode export class ParserNodeLeftValue extends ParserNodeExpression { type: string = 'ParserNodeLeftValue'; - generateAssign(expr: ParserNodeExpression): string { + generateAssign(context: ParserNodeGenerateCodeContext, expr: ParserNodeExpression): string { throw (new Error("Must implement")); } } @@ -142,11 +167,11 @@ export class ParserNodeIdentifier extends ParserNodeLeftValue implements ParseNo super(); } - generateAssign(expr: ParserNodeExpression) { - return 'runtimeContext.scopeSet(' + JSON.stringify(this.value) + ', ' + expr.generateCode() + ')'; + generateAssign(context: ParserNodeGenerateCodeContext, expr: ParserNodeExpression) { + return 'runtimeContext.scopeSet(' + JSON.stringify(this.value) + ', ' + expr.generateCode(context) + ')'; } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { return 'runtimeContext.scopeGet(' + JSON.stringify(this.value) + ')'; } } @@ -158,11 +183,12 @@ export class ParserNodeStatement extends ParserNode { export class ParserNodeRaw extends ParserNodeExpression { type: string = 'ParserNodeRaw'; - constructor(public value: string) { + constructor(public value: string, public putAlways: bool = true) { super(); } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { + if (!context.doWrite && !this.putAlways) return ''; return this.value; } } @@ -174,8 +200,8 @@ export class ParserNodeStatementExpression extends ParserNodeStatement { super(); } - generateCode() { - return this.expression.generateCode() + ';'; + generateCode(context: ParserNodeGenerateCodeContext) { + return this.expression.generateCode(context) + ';'; } } @@ -186,8 +212,8 @@ export class ParserNodeAssignment extends ParserNodeExpression { super(); } - generateCode() { - return this.leftValue.generateAssign(this.rightValue); + generateCode(context: ParserNodeGenerateCodeContext) { + return this.leftValue.generateAssign(context, this.rightValue); } } @@ -201,8 +227,8 @@ export class ParserNodeCommaExpression extends ParserNode { super(); } - generateCode() { - return this.expressions.map((item) => item.generateCode()).join(', '); + generateCode(context: ParserNodeGenerateCodeContext) { + return this.expressions.map((item) => item.generateCode(context)).join(', '); } } @@ -216,8 +242,8 @@ export class ParserNodeArrayAccess extends ParserNodeExpression { super(); } - generateCode() { - return 'runtimeContext.access(' + this.object.generateCode() + ', ' + this.key.generateCode() + ')'; + generateCode(context: ParserNodeGenerateCodeContext) { + return 'runtimeContext.access(' + this.object.generateCode(context) + ', ' + this.key.generateCode(context) + ')'; } } @@ -228,8 +254,8 @@ export class ParserNodeArraySlice extends ParserNodeExpression { super(); } - generateCode() { - return 'runtimeContext.slice(' + this.object.generateCode() + ', ' + this.left.generateCode() + ', ' + this.right.generateCode() + ')'; + generateCode(context: ParserNodeGenerateCodeContext) { + return 'runtimeContext.slice(' + this.object.generateCode(context) + ', ' + this.left.generateCode(context) + ', ' + this.right.generateCode(context) + ')'; } } @@ -240,12 +266,12 @@ export class ParserNodeFunctionCall extends ParserNodeExpression { super(); } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { if (this.functionExpr instanceof ParserNodeArrayAccess) { var arrayAccess = this.functionExpr; - return 'runtimeContext.callContext(' + arrayAccess.object.generateCode() + ', ' + arrayAccess.key.generateCode() + ', [' + this.arguments.generateCode() + '], ' + JSON.stringify(this.arguments.names) + ')'; + return 'runtimeContext.callContext(' + arrayAccess.object.generateCode(context) + ', ' + arrayAccess.key.generateCode(context) + ', [' + this.arguments.generateCode(context) + '], ' + JSON.stringify(this.arguments.names) + ')'; } else { - return 'runtimeContext.call(' + this.functionExpr.generateCode() + ', [' + this.arguments.generateCode() + '], ' + JSON.stringify(this.arguments.names) + ')'; + return 'runtimeContext.call(' + this.functionExpr.generateCode(context) + ', [' + this.arguments.generateCode(context) + '], ' + JSON.stringify(this.arguments.names) + ')'; } } } @@ -257,8 +283,8 @@ export class ParserNodeFilterCall extends ParserNodeExpression { super(); } - generateCode() { - return 'runtimeContext.filter(' + JSON.stringify(this.filterName) + ', [' + this.arguments.generateCode() + '])'; + generateCode(context: ParserNodeGenerateCodeContext) { + return 'runtimeContext.filter(' + JSON.stringify(this.filterName) + ', [' + this.arguments.generateCode(context) + '])'; } } @@ -272,12 +298,12 @@ export class ParserNodeUnaryOperation extends ParserNodeExpression { super(); } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { switch (this.operator) { case 'not': - return '!(' + this.right.generateCode() + ')'; + return '!(' + this.right.generateCode(context) + ')'; default: - return this.operator + '(' + this.right.generateCode() + ')'; + return this.operator + '(' + this.right.generateCode(context) + ')'; } } } @@ -292,7 +318,7 @@ export class ParserNodeBinaryOperation extends ParserNodeExpression { super(); } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { switch (this.operator) { case 'b-or': return '("" + ' + this.left.generateCode() + ' | ' + this.right.generateCode() + ')'; @@ -328,11 +354,11 @@ export class ParserNodeBinaryOperation extends ParserNodeExpression { if (right instanceof ParserNodeFunctionCall) { //throw (new Error("Not implemented ParserNodeFunctionCall")); - ret = 'runtimeContext.test(' + (right).functionExpr.generateCode() + ', [' + left.generateCode() + ',' + (right).arguments.generateCode() + '])'; + ret = 'runtimeContext.test(' + (right).functionExpr.generateCode(context) + ', [' + left.generateCode(context) + ',' + (right).arguments.generateCode(context) + '])'; } else if (right instanceof ParserNodeIdentifier) { - ret = 'runtimeContext.test(' + JSON.stringify((right).value) + ', [' + left.generateCode() + '])'; + ret = 'runtimeContext.test(' + JSON.stringify((right).value) + ', [' + left.generateCode(context) + '])'; } else if (right instanceof ParserNodeLiteral && (right).value === null) { - ret = 'runtimeContext.test("null", [' + left.generateCode() + '])'; + ret = 'runtimeContext.test("null", [' + left.generateCode(context) + '])'; } else { throw (new Error("ParserNodeBinaryOperation: Not implemented 'is' operator for tests with " + JSON.stringify(right))); } @@ -362,12 +388,12 @@ export class ParserNodeTernaryOperation extends ParserNode { super(); } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { return ( '(' + - this.cond.generateCode() + - " ? " + this.exprTrue.generateCode() + - " : " + this.exprFalse.generateCode() + + this.cond.generateCode(context) + + " ? " + this.exprTrue.generateCode(context) + + " : " + this.exprFalse.generateCode(context) + ')' ); } @@ -383,7 +409,8 @@ export class ParserNodeOutputText extends ParserNode { super(); } - generateCode() { + generateCode(context: ParserNodeGenerateCodeContext) { + if (!context.doWrite) return ''; return 'runtimeContext.write(' + JSON.stringify(this.text) + ');'; } } @@ -395,8 +422,9 @@ export class ParserNodeOutputNodeExpression extends ParserNodeExpression { super(); } - generateCode() { - return 'runtimeContext.write(' + this.expression.generateCode() + ')'; + generateCode(context: ParserNodeGenerateCodeContext) { + if (!context.doWrite) return ''; + return 'runtimeContext.write(' + this.expression.generateCode(context) + ')'; } } @@ -407,8 +435,8 @@ export class ParserNodeReturnStatement extends ParserNodeStatement { super(); } - generateCode() { - return 'return ' + this.expression.generateCode() + ';'; + generateCode(context: ParserNodeGenerateCodeContext) { + return 'return ' + this.expression.generateCode(context) + ';'; } } diff --git a/lib/parser/TemplateParser.js b/lib/parser/TemplateParser.js index b02d0c9..5a9fd4c 100644 --- a/lib/parser/TemplateParser.js +++ b/lib/parser/TemplateParser.js @@ -1,217 +1,231 @@ -var ParserNode = require('./ParserNode') -var TokenReader = require('../lexer/TokenReader') -var _TemplateTokenizer = require('../lexer/TemplateTokenizer') - -var RuntimeContext = require('../runtime/RuntimeContext') -var TokenParserContext = require('./TokenParserContext') -var ExpressionParser = require('./ExpressionParser') - -var SandboxPolicy = require('../SandboxPolicy') -var TemplateTokenizer = _TemplateTokenizer.TemplateTokenizer; -exports.FlowException = function (blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { - this.blockType = blockType; - this.templateParser = templateParser; - this.tokenParserContext = tokenParserContext; - this.templateTokenReader = templateTokenReader; - this.expressionTokenReader = expressionTokenReader; -}; -exports.FlowException.prototype['__proto__'] = Error.prototype; -function debug(data) { -} -var TemplateParser = (function () { - function TemplateParser(templateProvider, languageContext) { - this.templateProvider = templateProvider; - this.languageContext = languageContext; - this.registry = { - }; - this.registryString = { - }; - this.sandboxPolicy = new SandboxPolicy.SandboxPolicy(); - } - TemplateParser.prototype.getCache = function () { - return this.languageContext.templateConfig.getCache(); - }; - TemplateParser.prototype.compileAndRenderStringToString = function (content, scope, tokenParserContextCommon) { - if(scope === undefined) { - scope = { - }; - } - var runtimeContext = new RuntimeContext.RuntimeContext(this, scope, this.languageContext); - this.compileAndRenderString(content, runtimeContext, tokenParserContextCommon); - return runtimeContext.output; - }; - TemplateParser.prototype.compileAndRenderToString = function (path, scope, tokenParserContextCommon) { - if(scope === undefined) { - scope = { - }; - } - var runtimeContext = new RuntimeContext.RuntimeContext(this, scope, this.languageContext); - this.compileAndRender(path, runtimeContext, tokenParserContextCommon); - return runtimeContext.output; - }; - TemplateParser.prototype.compileAndRenderString = function (content, runtimeContext, tokenParserContextCommon) { - var template = new (this.compileString(content, runtimeContext, tokenParserContextCommon).class)(); - template.render(runtimeContext); - return template; - }; - TemplateParser.prototype.compileAndRender = function (path, runtimeContext, tokenParserContextCommon) { - var template = new (this.compile(path, runtimeContext, tokenParserContextCommon).class)(); - template.render(runtimeContext); - return template; - }; - TemplateParser.prototype.getEvalCodeString = function (content, path, tokenParserContextCommon) { - var templateTokenizer = new TemplateTokenizer(content); - var templateTokens = templateTokenizer.tokenize(); - var tokenParserContext = new TokenParserContext.TokenParserContext(tokenParserContextCommon || new TokenParserContext.TokenParserContextCommon(), this.sandboxPolicy); - try { - tokenParserContext.setBlock('__main', this.parseTemplateSync(tokenParserContext, new TokenReader.TokenReader(templateTokens))); - } catch (e) { - if(e instanceof exports.FlowException) { - throw (new Error("Unexpected tag '" + e.blockType + "' on template root")); - } - throw (e); - } - var output = ''; - output += 'CurrentTemplate = function() { this.name = ' + JSON.stringify(path) + '; };\n'; - output += 'CurrentTemplate.prototype.render = function(runtimeContext) { runtimeContext.setTemplate(this); this.__main(runtimeContext); };\n'; - tokenParserContext.iterateBlocks(function (blockNode, blockName) { - output += 'CurrentTemplate.prototype.' + blockName + ' = function(runtimeContext) {\n'; - { - output += 'var that = this;\n'; - output += 'runtimeContext.setCurrentBlock(that, ' + JSON.stringify(blockName) + ', function() {'; - { - output += blockNode.generateCode() + "\n"; - } - output += '});'; - } - output += '};\n'; - }); - output += 'CurrentTemplate.prototype.macros = {};\n'; - output += 'CurrentTemplate.prototype.macros.$runtimeContext = runtimeContext;\n'; - tokenParserContext.iterateMacros(function (macroNode, macroName) { - output += 'CurrentTemplate.prototype.macros.' + macroName + ' = function() {\n'; - output += 'var runtimeContext = this.$runtimeContext || this;\n'; - output += macroNode.generateCode(); - output += '};\n'; - }); - debug(output); - return { - output: output, - tokenParserContext: tokenParserContext - }; - }; - TemplateParser.prototype.getEvalCode = function (path, tokenParserContextCommon) { - if(!this.getCache()) { - delete this.registry[path]; - } - if(this.registry[path] !== undefined) { - return this.registry[path]; - } - var content = this.templateProvider.getSync(path, this.getCache()); - return this.getEvalCodeString(content, path, tokenParserContextCommon); - }; - TemplateParser.prototype.compileString = function (content, runtimeContext, tokenParserContextCommon) { - runtimeContext.sandboxPolicy = this.sandboxPolicy; - if(!this.getCache()) { - delete this.registryString[content]; - } - if(this.registryString[content] === undefined) { - var info = this.getEvalCodeString(content, 'inline', tokenParserContextCommon); - var output = info.output; - var tokenParserContext = info.tokenParserContext; - var CurrentTemplate = undefined; - try { - eval(output); - } catch (e) { - console.log('----------------------------'); - console.log('Exception in eval: ' + output); - console.log('----------------------------'); - throw (e); - } - this.registryString[content] = { - output: output, - class: CurrentTemplate - }; - } - return this.registryString[content]; - }; - TemplateParser.prototype.compile = function (path, runtimeContext, tokenParserContextCommon) { - runtimeContext.sandboxPolicy = this.sandboxPolicy; - if(!this.getCache()) { - delete this.registry[path]; - } - if(this.registry[path] === undefined) { - var info = this.getEvalCode(path, tokenParserContextCommon); - var output = info.output; - var tokenParserContext = info.tokenParserContext; - var CurrentTemplate = undefined; - try { - eval(output); - } catch (e) { - console.log('----------------------------'); - console.log('Exception in eval: ' + output); - console.log('----------------------------'); - throw (e); - } - this.registry[path] = { - output: output, - class: CurrentTemplate - }; - } - return this.registry[path]; - }; - TemplateParser.prototype.parseTemplateSyncOne = function (tokenParserContext, tokenReader) { - if(!tokenReader.hasMore()) { - return null; - } - var item = tokenReader.peek(); - debug('parseTemplateSync: ' + item.type); - switch(item.type) { - case 'text': - item = tokenReader.read(); - return new ParserNode.ParserNodeRaw('runtimeContext.write(' + JSON.stringify(String(item.value)) + ');'); - case 'trimSpacesAfter': - case 'trimSpacesBefore': - item = tokenReader.read(); - return (new ParserNode.ParserNodeRaw('runtimeContext.trimSpaces();')); - case 'expression': - item = tokenReader.read(); - return (new ParserNode.ParserNodeRaw(this.parseTemplateExpressionSync(tokenParserContext, tokenReader, new TokenReader.TokenReader(item.value)).generateCode())); - case 'block': - item = tokenReader.read(); - return (this.parseTemplateBlockSync(tokenParserContext, tokenReader, new TokenReader.TokenReader(item.value))); - } - throw (new Error("Invalid item.type == '" + item.type + "'")); - }; - TemplateParser.prototype.parseTemplateSync = function (tokenParserContext, tokenReader) { - var nodes = new ParserNode.ParserNodeContainer(); - while(tokenReader.hasMore()) { - nodes.add(this.parseTemplateSyncOne(tokenParserContext, tokenReader)); - } - return nodes; - }; - TemplateParser.prototype.parseTemplateExpressionSync = function (tokenParserContext, templateTokenReader, expressionTokenReader) { - return new ParserNode.ParserNodeStatementExpression(new ParserNode.ParserNodeWriteExpression((new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression())); - }; - TemplateParser.prototype.parseTemplateBlockSync = function (tokenParserContext, templateTokenReader, expressionTokenReader) { - var that = this; - var blockTypeToken = expressionTokenReader.read(); - var blockType = blockTypeToken.value; - if(blockTypeToken.type != 'id') { - throw (new Error("Block expected a type as first token but found : " + JSON.stringify(blockTypeToken))); - } - debug('BLOCK: ' + blockType); - var blockHandler = this.languageContext.tags[blockType]; - if(blockHandler !== undefined) { - if(tokenParserContext.common.sandbox) { - if(this.sandboxPolicy.allowedTags.indexOf(blockType) == -1) { - throw (new Error("Sandbox policy disallows block '" + blockType + "'")); - } - } - return blockHandler(blockType, this, tokenParserContext, templateTokenReader, expressionTokenReader); - } - throw (new Error("Invalid block type '" + blockTypeToken.value + "'")); - }; - return TemplateParser; -})(); -exports.TemplateParser = TemplateParser; +var ParserNode = require('./ParserNode') +var TokenReader = require('../lexer/TokenReader') +var _TemplateTokenizer = require('../lexer/TemplateTokenizer') + +var RuntimeContext = require('../runtime/RuntimeContext') +var TokenParserContext = require('./TokenParserContext') +var ExpressionParser = require('./ExpressionParser') + +var SandboxPolicy = require('../SandboxPolicy') +var TemplateTokenizer = _TemplateTokenizer.TemplateTokenizer; +exports.FlowException = function (blockType, templateParser, tokenParserContext, templateTokenReader, expressionTokenReader) { + this.blockType = blockType; + this.templateParser = templateParser; + this.tokenParserContext = tokenParserContext; + this.templateTokenReader = templateTokenReader; + this.expressionTokenReader = expressionTokenReader; +}; +exports.FlowException.prototype['__proto__'] = Error.prototype; +function debug(data) { +} +var TemplateParser = (function () { + function TemplateParser(templateProvider, languageContext) { + this.templateProvider = templateProvider; + this.languageContext = languageContext; + this.registry = { + }; + this.registryString = { + }; + this.sandboxPolicy = new SandboxPolicy.SandboxPolicy(); + } + TemplateParser.prototype.getCache = function () { + return this.languageContext.templateConfig.getCache(); + }; + TemplateParser.prototype.compileAndRenderStringToString = function (content, scope, tokenParserContextCommon) { + if(scope === undefined) { + scope = { + }; + } + var runtimeContext = new RuntimeContext.RuntimeContext(this, scope, this.languageContext); + this.compileAndRenderString(content, runtimeContext, tokenParserContextCommon); + return runtimeContext.output; + }; + TemplateParser.prototype.compileAndRenderToString = function (path, scope, tokenParserContextCommon) { + if(scope === undefined) { + scope = { + }; + } + var runtimeContext = new RuntimeContext.RuntimeContext(this, scope, this.languageContext); + this.compileAndRender(path, runtimeContext, tokenParserContextCommon); + return runtimeContext.output; + }; + TemplateParser.prototype.compileAndRenderString = function (content, runtimeContext, tokenParserContextCommon) { + var template = new (this.compileString(content, runtimeContext, tokenParserContextCommon).class)(); + template.render(runtimeContext); + return template; + }; + TemplateParser.prototype.compileAndRender = function (path, runtimeContext, tokenParserContextCommon) { + var template = new (this.compile(path, runtimeContext, tokenParserContextCommon).class)(); + template.render(runtimeContext); + return template; + }; + TemplateParser.prototype.getEvalCodeString = function (content, path, tokenParserContextCommon) { + var templateTokenizer = new TemplateTokenizer(content); + var templateTokens = templateTokenizer.tokenize(); + var tokenParserContext = new TokenParserContext.TokenParserContext(tokenParserContextCommon || new TokenParserContext.TokenParserContextCommon(), this.sandboxPolicy); + try { + tokenParserContext.setBlock('__main', this.parseTemplateSync(tokenParserContext, new TokenReader.TokenReader(templateTokens))); + } catch (e) { + if(e instanceof exports.FlowException) { + throw (new Error("Unexpected tag '" + e.blockType + "' on template root")); + } + throw (e); + } + var output = ''; + output += 'CurrentTemplate = function() { this.name = ' + JSON.stringify(path) + '; };\n'; + output += 'CurrentTemplate.prototype.render = function(runtimeContext) { runtimeContext.setTemplate(this); this.__main(runtimeContext); };\n'; + tokenParserContext.iterateBlocks(function (blockNode, blockName) { + output += 'CurrentTemplate.prototype.' + blockName + ' = function(runtimeContext) {\n'; + { + var avoidOutput = ((blockName == "__main") && (tokenParserContext.afterMainNodes.length > 0)); + output += 'var that = this;\n'; + output += 'runtimeContext.setCurrentBlock(that, ' + JSON.stringify(blockName) + ', function() {'; + { + output += blockNode.generateCode({ + doWrite: !avoidOutput + }) + "\n"; + } + if(avoidOutput) { + tokenParserContext.iterateAfterMainNodes(function (blockNode2) { + output += blockNode2.generateCode({ + doWrite: false + }) + "\n"; + }); + } + output += '});'; + } + output += '};\n'; + }); + output += 'CurrentTemplate.prototype.macros = {};\n'; + output += 'CurrentTemplate.prototype.macros.$runtimeContext = runtimeContext;\n'; + tokenParserContext.iterateMacros(function (macroNode, macroName) { + output += 'CurrentTemplate.prototype.macros.' + macroName + ' = function() {\n'; + output += 'var runtimeContext = this.$runtimeContext || this;\n'; + output += macroNode.generateCode({ + doWrite: true + }); + output += '};\n'; + }); + debug(output); + return { + output: output, + tokenParserContext: tokenParserContext + }; + }; + TemplateParser.prototype.getEvalCode = function (path, tokenParserContextCommon) { + if(!this.getCache()) { + delete this.registry[path]; + } + if(this.registry[path] !== undefined) { + return this.registry[path]; + } + var content = this.templateProvider.getSync(path, this.getCache()); + return this.getEvalCodeString(content, path, tokenParserContextCommon); + }; + TemplateParser.prototype.compileString = function (content, runtimeContext, tokenParserContextCommon) { + runtimeContext.sandboxPolicy = this.sandboxPolicy; + if(!this.getCache()) { + delete this.registryString[content]; + } + if(this.registryString[content] === undefined) { + var info = this.getEvalCodeString(content, 'inline', tokenParserContextCommon); + var output = info.output; + var tokenParserContext = info.tokenParserContext; + var CurrentTemplate = undefined; + try { + eval(output); + } catch (e) { + console.log('----------------------------'); + console.log('Exception in eval: ' + output); + console.log('----------------------------'); + throw (e); + } + this.registryString[content] = { + output: output, + class: CurrentTemplate + }; + } + return this.registryString[content]; + }; + TemplateParser.prototype.compile = function (path, runtimeContext, tokenParserContextCommon) { + runtimeContext.sandboxPolicy = this.sandboxPolicy; + if(!this.getCache()) { + delete this.registry[path]; + } + if(this.registry[path] === undefined) { + var info = this.getEvalCode(path, tokenParserContextCommon); + var output = info.output; + var tokenParserContext = info.tokenParserContext; + var CurrentTemplate = undefined; + try { + eval(output); + } catch (e) { + console.log('----------------------------'); + console.log('Exception in eval: ' + output); + console.log('----------------------------'); + throw (e); + } + this.registry[path] = { + output: output, + class: CurrentTemplate + }; + } + return this.registry[path]; + }; + TemplateParser.prototype.parseTemplateSyncOne = function (tokenParserContext, tokenReader) { + if(!tokenReader.hasMore()) { + return null; + } + var item = tokenReader.peek(); + debug('parseTemplateSync: ' + item.type); + switch(item.type) { + case 'text': + item = tokenReader.read(); + return new ParserNode.ParserNodeOutputText(String(item.value)); + case 'trimSpacesAfter': + case 'trimSpacesBefore': + item = tokenReader.read(); + return (new ParserNode.ParserNodeRaw('runtimeContext.trimSpaces();')); + case 'expression': + item = tokenReader.read(); + return (new ParserNode.ParserNodeRaw(this.parseTemplateExpressionSync(tokenParserContext, tokenReader, new TokenReader.TokenReader(item.value)).generateCode({ + doWrite: true + }))); + case 'block': + item = tokenReader.read(); + return (this.parseTemplateBlockSync(tokenParserContext, tokenReader, new TokenReader.TokenReader(item.value))); + } + throw (new Error("Invalid item.type == '" + item.type + "'")); + }; + TemplateParser.prototype.parseTemplateSync = function (tokenParserContext, tokenReader) { + var nodes = new ParserNode.ParserNodeContainer(); + while(tokenReader.hasMore()) { + nodes.add(this.parseTemplateSyncOne(tokenParserContext, tokenReader)); + } + return nodes; + }; + TemplateParser.prototype.parseTemplateExpressionSync = function (tokenParserContext, templateTokenReader, expressionTokenReader) { + return new ParserNode.ParserNodeStatementExpression(new ParserNode.ParserNodeWriteExpression((new ExpressionParser.ExpressionParser(expressionTokenReader, tokenParserContext)).parseExpression())); + }; + TemplateParser.prototype.parseTemplateBlockSync = function (tokenParserContext, templateTokenReader, expressionTokenReader) { + var that = this; + var blockTypeToken = expressionTokenReader.read(); + var blockType = blockTypeToken.value; + if(blockTypeToken.type != 'id') { + throw (new Error("Block expected a type as first token but found : " + JSON.stringify(blockTypeToken))); + } + debug('BLOCK: ' + blockType); + var blockHandler = this.languageContext.tags[blockType]; + if(blockHandler !== undefined) { + if(tokenParserContext.common.sandbox) { + if(this.sandboxPolicy.allowedTags.indexOf(blockType) == -1) { + throw (new Error("Sandbox policy disallows block '" + blockType + "'")); + } + } + return blockHandler(blockType, this, tokenParserContext, templateTokenReader, expressionTokenReader); + } + throw (new Error("Invalid block type '" + blockTypeToken.value + "'")); + }; + return TemplateParser; +})(); +exports.TemplateParser = TemplateParser; diff --git a/lib/parser/TemplateParser.ts b/lib/parser/TemplateParser.ts index 93cb13a..ffba488 100644 --- a/lib/parser/TemplateParser.ts +++ b/lib/parser/TemplateParser.ts @@ -95,10 +95,17 @@ export class TemplateParser { tokenParserContext.iterateBlocks((blockNode, blockName) => { output += 'CurrentTemplate.prototype.' + blockName + ' = function(runtimeContext) {\n'; { + var avoidOutput = ((blockName == "__main") && (tokenParserContext.afterMainNodes.length > 0)); + output += 'var that = this;\n'; output += 'runtimeContext.setCurrentBlock(that, ' + JSON.stringify(blockName) + ', function() {'; { - output += blockNode.generateCode() + "\n"; + output += blockNode.generateCode({ doWrite: !avoidOutput }) + "\n"; + } + if (avoidOutput) { + tokenParserContext.iterateAfterMainNodes((blockNode2) => { + output += blockNode2.generateCode({ doWrite: false }) + "\n"; + }); } output += '});'; } @@ -112,7 +119,7 @@ export class TemplateParser { output += 'CurrentTemplate.prototype.macros.' + macroName + ' = function() {\n'; output += 'var runtimeContext = this.$runtimeContext || this;\n'; //output += 'console.log("<<<<<<<<<<<<<<<<<<<<<<");console.log(this);\n'; - output += macroNode.generateCode(); + output += macroNode.generateCode({ doWrite: true }); output += '};\n'; }); @@ -199,7 +206,7 @@ export class TemplateParser { switch (item.type) { case 'text': item = tokenReader.read(); - return new ParserNode.ParserNodeRaw('runtimeContext.write(' + JSON.stringify(String(item.value)) + ');'); + return new ParserNode.ParserNodeOutputText(String(item.value)); case 'trimSpacesAfter': case 'trimSpacesBefore': item = tokenReader.read(); @@ -207,7 +214,7 @@ export class TemplateParser { case 'expression': item = tokenReader.read(); // Propagate the "not done". - return (new ParserNode.ParserNodeRaw(this.parseTemplateExpressionSync(tokenParserContext, tokenReader, new TokenReader.TokenReader(item.value)).generateCode())); + return (new ParserNode.ParserNodeRaw(this.parseTemplateExpressionSync(tokenParserContext, tokenReader, new TokenReader.TokenReader(item.value)).generateCode({ doWrite: true }))); case 'block': item = tokenReader.read(); // Propagate the "not done". diff --git a/lib/parser/TokenParserContext.js b/lib/parser/TokenParserContext.js index a746573..413bb4f 100644 --- a/lib/parser/TokenParserContext.js +++ b/lib/parser/TokenParserContext.js @@ -1,61 +1,68 @@ - -var RuntimeUtils = require('../runtime/RuntimeUtils') - -var TokenParserContextCommon = (function () { - function TokenParserContextCommon(info) { - if (typeof info === "undefined") { info = { - }; } - this.sandbox = false; - if(RuntimeUtils.isObject(info)) { - for(var key in info) { - this[key] = info[key]; - } - } - } - TokenParserContextCommon.prototype.serialize = function () { - var ret = { - }; - for(var key in this) { - ret[key] = this[key]; - } - return ret; - }; - TokenParserContextCommon.prototype.setSandbox = function (callback) { - this.sandbox = true; - try { - callback(); - }finally { - this.sandbox = false; - } - }; - return TokenParserContextCommon; -})(); -exports.TokenParserContextCommon = TokenParserContextCommon; -var TokenParserContext = (function () { - function TokenParserContext(common, sandboxPolicy) { - this.common = common; - this.sandboxPolicy = sandboxPolicy; - this.blocksOutput = { - }; - this.macrosOutput = { - }; - } - TokenParserContext.prototype.iterateBlocks = function (callback) { - for(var name in this.blocksOutput) { - callback(this.blocksOutput[name], name); - } - }; - TokenParserContext.prototype.iterateMacros = function (callback) { - for(var name in this.macrosOutput) { - callback(this.macrosOutput[name], name); - } - }; - TokenParserContext.prototype.setBlock = function (blockName, node) { - return this.blocksOutput[blockName] = node; - }; - TokenParserContext.prototype.setMacro = function (macroName, node) { - return this.macrosOutput[macroName] = node; - }; - return TokenParserContext; -})(); -exports.TokenParserContext = TokenParserContext; + +var RuntimeUtils = require('../runtime/RuntimeUtils') + +var TokenParserContextCommon = (function () { + function TokenParserContextCommon(info) { + if (typeof info === "undefined") { info = { + }; } + this.sandbox = false; + if(RuntimeUtils.isObject(info)) { + for(var key in info) { + this[key] = info[key]; + } + } + } + TokenParserContextCommon.prototype.serialize = function () { + var ret = { + }; + for(var key in this) { + ret[key] = this[key]; + } + return ret; + }; + TokenParserContextCommon.prototype.setSandbox = function (callback) { + this.sandbox = true; + try { + callback(); + }finally { + this.sandbox = false; + } + }; + return TokenParserContextCommon; +})(); +exports.TokenParserContextCommon = TokenParserContextCommon; +var TokenParserContext = (function () { + function TokenParserContext(common, sandboxPolicy) { + this.common = common; + this.sandboxPolicy = sandboxPolicy; + this.blocksOutput = { + }; + this.macrosOutput = { + }; + this.afterMainNodes = []; + } + TokenParserContext.prototype.iterateBlocks = function (callback) { + for(var name in this.blocksOutput) { + callback(this.blocksOutput[name], name); + } + }; + TokenParserContext.prototype.iterateMacros = function (callback) { + for(var name in this.macrosOutput) { + callback(this.macrosOutput[name], name); + } + }; + TokenParserContext.prototype.iterateAfterMainNodes = function (callback) { + this.afterMainNodes.forEach(callback); + }; + TokenParserContext.prototype.setBlock = function (blockName, node) { + return this.blocksOutput[blockName] = node; + }; + TokenParserContext.prototype.setMacro = function (macroName, node) { + return this.macrosOutput[macroName] = node; + }; + TokenParserContext.prototype.addAfterMainNode = function (node) { + this.afterMainNodes.push(node); + }; + return TokenParserContext; +})(); +exports.TokenParserContext = TokenParserContext; diff --git a/lib/parser/TokenParserContext.ts b/lib/parser/TokenParserContext.ts index d35af7a..7abbd37 100644 --- a/lib/parser/TokenParserContext.ts +++ b/lib/parser/TokenParserContext.ts @@ -30,6 +30,7 @@ export class TokenParserContextCommon { export class TokenParserContext { private blocksOutput: any = {}; private macrosOutput: any = {}; + afterMainNodes: ParserNode.ParserNode[] = []; constructor(public common: TokenParserContextCommon, public sandboxPolicy: SandboxPolicy.SandboxPolicy) { } @@ -42,6 +43,10 @@ export class TokenParserContext { for (var name in this.macrosOutput) callback(this.macrosOutput[name], name); } + iterateAfterMainNodes(callback: (node: ParserNode.ParserNode) => void ) { + this.afterMainNodes.forEach(callback); + } + setBlock(blockName, node: ParserNode.ParserNode) { return this.blocksOutput[blockName] = node; } @@ -49,4 +54,8 @@ export class TokenParserContext { setMacro(macroName, node: ParserNode.ParserNode) { return this.macrosOutput[macroName] = node; } + + addAfterMainNode(node: ParserNode.ParserNode) { + this.afterMainNodes.push(node); + } } diff --git a/test/fixtures/regressions/issue_58.set b/test/fixtures/regressions/issue_58.set new file mode 100644 index 0000000..39acbbf --- /dev/null +++ b/test/fixtures/regressions/issue_58.set @@ -0,0 +1,27 @@ +=== TITLE +issue #58 + +=== DESCRIPTION +since extending ignores all non-block bodies, the import statement (that is executed at runtime) does not reach. + +=== INPUT +{} + +=== TEMPLATE:main +{% extends "layout" %} +{% import "macros" as macros %} +{% block body %} +{{ macros.foo(1, 2, 3) }} +{% endblock %} + +=== TEMPLATE:layout +{% block body %} +{% endblock %} + +=== TEMPLATE:macros +{% macro foo(a, b, c) %} + {{- "#{a}, #{b}, #{c}" -}} +{% endmacro %} + +=== OUTPUT +1, 2, 3 diff --git a/test/fixtures_test.js b/test/fixtures_test.js index a5e86ca..cdfb717 100644 --- a/test/fixtures_test.js +++ b/test/fixtures_test.js @@ -1,3 +1,8 @@ +var __extends = this.__extends || function (d, b) { + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; var assert = require('assert') var fs = require('fs') var TemplateParser = require('../lib/parser/TemplateParser.js').TemplateParser; @@ -7,10 +12,30 @@ var TemplateConfig = require('../lib/TemplateConfig.js').TemplateConfig; var RuntimeContext = require('../lib/runtime/RuntimeContext.js').RuntimeContext; var RuntimeUtils = require('../lib/runtime/RuntimeUtils.js'); var Default = require('../lib/lang/Default.js'); +var B = (function () { + function B() { } + B.prototype.test = function () { + console.log('B'); + }; + return B; +})(); +var A = (function (_super) { + __extends(A, _super); + function A() { + _super.apply(this, arguments); + + } + A.prototype.test = function () { + console.log('A'); + _super.prototype.test.call(this); + }; + return A; +})(B); function handleSet(name, data) { var parts = data.split('==='); var test = { title: 'untitled: ' + name, + description: '', input: { }, expected: '', @@ -22,7 +47,7 @@ function handleSet(name, data) { }; for(var n = 0; n < parts.length; n++) { var part = parts[n].trim(); - var token = /^([\w:]+)\s+([\S\s]*)$/gim.exec(part); + var token = /^([\w:]+)\s+([\S\s]*)$/igm.exec(part); if(token != null) { var key = token[1].trim().toLowerCase(); var value = token[2].trim(); @@ -30,6 +55,9 @@ function handleSet(name, data) { case 'title': test.title = value + ' (' + name + ')'; break; + case 'description': + test.description = value; + break; case 'input': test.input = JSON.parse(value); break; diff --git a/test/fixtures_test.ts b/test/fixtures_test.ts index a5d6681..fcede88 100644 --- a/test/fixtures_test.ts +++ b/test/fixtures_test.ts @@ -11,9 +11,17 @@ var RuntimeContext = require('../lib/runtime/RuntimeContext.js').RuntimeContext; var RuntimeUtils = require('../lib/runtime/RuntimeUtils.js'); var Default = require('../lib/lang/Default.js'); +class B { + test() { console.log('B'); } +} + +class A extends B { + test() { console.log('A'); super.test(); } +} + function handleSet(name, data) { var parts = data.split('==='); - var test = { title: 'untitled: ' + name, input: {}, expected: '', templates: {}, eval: undefined, eval_after: undefined, exception: undefined }; + var test = { title: 'untitled: ' + name, description: '', input: {}, expected: '', templates: {}, eval: undefined, eval_after: undefined, exception: undefined }; for (var n = 0; n < parts.length; n++) { var part = parts[n].trim(); var token = /^([\w:]+)\s+([\S\s]*)$/igm.exec(part); @@ -25,6 +33,7 @@ function handleSet(name, data) { var value = token[2].trim(); switch (key) { case 'title': test.title = value + ' (' + name + ')'; break; + case 'description': test.description = value; break; case 'input': test.input = JSON.parse(value); break; case 'output': test.expected = value; break; case 'eval': test.eval = value; break;