diff options
Diffstat (limited to 'lib/handlebars/compiler')
-rw-r--r-- | lib/handlebars/compiler/ast.js | 25 | ||||
-rw-r--r-- | lib/handlebars/compiler/compiler.js | 118 | ||||
-rw-r--r-- | lib/handlebars/compiler/helpers.js | 8 | ||||
-rw-r--r-- | lib/handlebars/compiler/javascript-compiler.js | 4 | ||||
-rw-r--r-- | lib/handlebars/compiler/printer.js | 33 | ||||
-rw-r--r-- | lib/handlebars/compiler/visitor.js | 21 |
6 files changed, 104 insertions, 105 deletions
diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index 7530074..2439571 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -66,17 +66,10 @@ var AST = { SexprNode: function(rawParams, hash, locInfo) { this.loc = locInfo; - this.type = "sexpr"; - this.hash = hash; - - this.id = rawParams[0]; + this.type = 'SubExpression'; + this.path = rawParams[0]; this.params = rawParams.slice(1); - }, - - HashNode: function(pairs, locInfo) { - this.loc = locInfo; - this.type = "hash"; - this.pairs = pairs; + this.hash = hash; }, PathNode: function(data, parts, locInfo) { @@ -128,6 +121,18 @@ var AST = { this.loc = locInfo; this.type = 'BooleanLiteral'; this.value = bool === 'true'; + }, + + HashNode: function(pairs, locInfo) { + this.loc = locInfo; + this.type = 'Hash'; + this.pairs = pairs; + }, + HashPair: function(key, value, locInfo) { + this.loc = locInfo; + this.type = 'HashPair'; + this.key = key; + this.value = value; } }; diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js index 5dbbfc4..e73ce37 100644 --- a/lib/handlebars/compiler/compiler.js +++ b/lib/handlebars/compiler/compiler.js @@ -11,16 +11,16 @@ function helperExpr(sexpr) { return !!(sexpr.isHelper || sexpr.params.length || sexpr.hash); } -function scopedId(id) { - return (/^\.|this\b/).test(id.original); +function scopedId(path) { + return (/^\.|this\b/).test(path.original); } // an ID is simple if it only has one part, and that part is not // `..` or `this`. -function simpleId(id) { - var part = id.parts[0]; +function simpleId(path) { + var part = path.parts[0]; - return id.parts.length === 1 && !scopedId(id) && !id.depth; + return path.parts.length === 1 && !scopedId(path) && !path.depth; } export function Compiler() {} @@ -147,7 +147,7 @@ Compiler.prototype = { this.opcode('pushProgram', block, program); this.opcode('pushProgram', block, inverse); this.opcode('emptyHash', block); - this.opcode('blockValue', block, sexpr.id.original); + this.opcode('blockValue', block, sexpr.path.original); } else { this.ambiguousSexpr(sexpr, program, inverse); @@ -163,7 +163,7 @@ Compiler.prototype = { }, PartialStatement: function(partial) { - var partialName = partial.sexpr.id.original; + var partialName = partial.sexpr.path.original; this.usePartial = true; if (partial.sexpr.hash) { @@ -194,7 +194,7 @@ Compiler.prototype = { }, MustacheStatement: function(mustache) { - this.sexpr(mustache.sexpr); + this.accept(mustache.sexpr); if(mustache.escaped && !this.options.noEscape) { this.opcode('appendEscaped', mustache); @@ -211,91 +211,67 @@ Compiler.prototype = { CommentStatement: function() {}, - sexpr: function(sexpr) { + SubExpression: function(sexpr) { var type = this.classifySexpr(sexpr); - if (type === "simple") { + if (type === 'simple') { this.simpleSexpr(sexpr); - } else if (type === "helper") { + } else if (type === 'helper') { this.helperSexpr(sexpr); } else { this.ambiguousSexpr(sexpr); } }, ambiguousSexpr: function(sexpr, program, inverse) { - var id = sexpr.id, - name = id.parts[0], + var path = sexpr.path, + name = path.parts[0], isBlock = program != null || inverse != null; - this.opcode('getContext', sexpr, id.depth); + this.opcode('getContext', sexpr, path.depth); this.opcode('pushProgram', sexpr, program); this.opcode('pushProgram', sexpr, inverse); - this.accept(id); + this.accept(path); this.opcode('invokeAmbiguous', sexpr, name, isBlock); }, simpleSexpr: function(sexpr) { - var id = sexpr.id; - - if (id.parts.length) { - this.accept(id); - } else { - // Simplified ID for `this` - this.addDepth(id.depth); - this.opcode('getContext', sexpr, id.depth); - this.opcode('pushContext', sexpr); - } - + this.accept(sexpr.path); this.opcode('resolvePossibleLambda', sexpr); }, helperSexpr: function(sexpr, program, inverse) { var params = this.setupFullMustacheParams(sexpr, program, inverse), - id = sexpr.id, - name = id.parts[0]; + path = sexpr.path, + name = path.parts[0]; if (this.options.knownHelpers[name]) { this.opcode('invokeKnownHelper', sexpr, params.length, name); } else if (this.options.knownHelpersOnly) { throw new Exception("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr); } else { - id.falsy = true; - - this.accept(id); - this.opcode('invokeHelper', sexpr, params.length, id.original, simpleId(id)); - } - }, - - hash: function(hash) { - var pairs = hash.pairs, i, l; - - this.opcode('pushHash', hash); + path.falsy = true; - for(i=0, l=pairs.length; i<l; i++) { - this.pushParam(pairs[i][1]); + this.accept(path); + this.opcode('invokeHelper', sexpr, params.length, path.original, simpleId(path)); } - while(i--) { - this.opcode('assignToHash', hash, pairs[i][0]); - } - this.opcode('popHash', hash); }, - PathExpression: function(id) { - this.addDepth(id.depth); - this.opcode('getContext', id, id.depth); + PathExpression: function(path) { + this.addDepth(path.depth); + this.opcode('getContext', path, path.depth); - var name = id.parts[0]; + var name = path.parts[0]; if (!name) { // Context reference, i.e. `{{foo .}}` or `{{foo ..}}` - this.opcode('pushContext', id); - } else if (id.data) { + this.opcode('pushContext', path); + } else if (path.data) { this.options.data = true; - this.opcode('lookupData', id, id.depth, id.parts); + this.opcode('lookupData', path, path.depth, path.parts); } else { - this.opcode('lookupOnContext', id, id.parts, id.falsy, scopedId(id)); + this.opcode('lookupOnContext', path, path.parts, path.falsy, scopedId(path)); } }, @@ -311,6 +287,20 @@ Compiler.prototype = { this.opcode('pushLiteral', bool, bool.value); }, + Hash: function(hash) { + var pairs = hash.pairs, i, l; + + this.opcode('pushHash', hash); + + for (i=0, l=pairs.length; i<l; i++) { + this.pushParam(pairs[i].value); + } + while (i--) { + this.opcode('assignToHash', hash, pairs[i].key); + } + this.opcode('popHash', hash); + }, + // HELPERS opcode: function(name, node) { this.opcodes.push({ opcode: name, args: slice.call(arguments, 2), loc: node.loc }); @@ -328,19 +318,19 @@ Compiler.prototype = { classifySexpr: function(sexpr) { // a mustache is an eligible helper if: // * its id is simple (a single part, not `this` or `..`) - var isHelper = helperExpr(sexpr); + var isHelper = helperExpr(sexpr); // if a mustache is an eligible helper but not a definite // helper, it is ambiguous, and will be resolved in a later // pass or at runtime. - var isEligible = isHelper || simpleId(sexpr.id); + var isEligible = isHelper || simpleId(sexpr.path); - var options = this.options; + var options = this.options; // if ambiguous, we can possibly resolve the ambiguity now // An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc. if (isEligible && !isHelper) { - var name = sexpr.id.parts[0]; + var name = sexpr.path.parts[0]; if (options.knownHelpers[name]) { isHelper = true; @@ -349,9 +339,9 @@ Compiler.prototype = { } } - if (isHelper) { return "helper"; } - else if (isEligible) { return "ambiguous"; } - else { return "simple"; } + if (isHelper) { return 'helper'; } + else if (isEligible) { return 'ambiguous'; } + else { return 'simple'; } }, pushParams: function(params) { @@ -364,7 +354,7 @@ Compiler.prototype = { var value = val.value != null ? val.value : val.original || ''; // Force helper evaluation - if (val.type === 'sexpr') { + if (val.type === 'SubExpression') { val.isHelper = true; } @@ -381,10 +371,10 @@ Compiler.prototype = { this.opcode('getContext', val, val.depth || 0); this.opcode('pushStringParam', val, value, val.type); - if (val.type === 'sexpr') { + if (val.type === 'SubExpression') { // Subexpressions get evaluated and passed in // in string params mode. - this.sexpr(val); + this.accept(val); } } else { if (this.trackIds) { @@ -409,7 +399,7 @@ Compiler.prototype = { this.opcode('pushProgram', sexpr, inverse); if (sexpr.hash) { - this.hash(sexpr.hash); + this.accept(sexpr.hash); } else { this.opcode('emptyHash', sexpr); } diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js index 023566b..f75c10b 100644 --- a/lib/handlebars/compiler/helpers.js +++ b/lib/handlebars/compiler/helpers.js @@ -26,10 +26,10 @@ export function stripComment(comment) { export function prepareRawBlock(openRawBlock, content, close, locInfo) { /*jshint -W040 */ - if (openRawBlock.sexpr.id.original !== close) { + if (openRawBlock.sexpr.path.original !== close) { var errorNode = {loc: openRawBlock.sexpr.loc}; - throw new Exception(openRawBlock.sexpr.id.original + " doesn't match " + close, errorNode); + throw new Exception(openRawBlock.sexpr.path.original + " doesn't match " + close, errorNode); } var program = new this.ProgramNode([content], null, {}, locInfo); @@ -40,10 +40,10 @@ export function prepareRawBlock(openRawBlock, content, close, locInfo) { export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { /*jshint -W040 */ // When we are chaining inverse calls, we will not have a close path - if (close && close.path && openBlock.sexpr.id.original !== close.path.original) { + if (close && close.path && openBlock.sexpr.path.original !== close.path.original) { var errorNode = {loc: openBlock.sexpr.loc}; - throw new Exception(openBlock.sexpr.id.original + ' doesn\'t match ' + close.path.original, errorNode); + throw new Exception(openBlock.sexpr.path.original + ' doesn\'t match ' + close.path.original, errorNode); } program.blockParams = openBlock.blockParams; diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 2916a32..cde2ab5 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -460,7 +460,7 @@ JavaScriptCompiler.prototype = { // If it's a subexpression, the string result // will be pushed after this opcode. - if (type !== 'sexpr') { + if (type !== 'SubExpression') { if (typeof string === 'string') { this.pushString(string); } else { @@ -661,7 +661,7 @@ JavaScriptCompiler.prototype = { pushId: function(type, name) { if (type === 'PathExpression') { this.pushString(name); - } else if (type === 'sexpr') { + } else if (type === 'SubExpression') { this.pushStackLiteral('true'); } else { this.pushStackLiteral('null'); diff --git a/lib/handlebars/compiler/printer.js b/lib/handlebars/compiler/printer.js index 05e8ee7..b549d61 100644 --- a/lib/handlebars/compiler/printer.js +++ b/lib/handlebars/compiler/printer.js @@ -75,7 +75,7 @@ PrintVisitor.prototype.BlockStatement = function(block) { PrintVisitor.prototype.PartialStatement = function(partial) { var sexpr = partial.sexpr, - content = 'PARTIAL:' + sexpr.id.original; + content = 'PARTIAL:' + sexpr.path.original; if(sexpr.params[0]) { content += ' ' + this.accept(sexpr.params[0]); } @@ -93,7 +93,7 @@ PrintVisitor.prototype.CommentStatement = function(comment) { return this.pad("{{! '" + comment.value + "' }}"); }; -PrintVisitor.prototype.sexpr = function(sexpr) { +PrintVisitor.prototype.SubExpression = function(sexpr) { var params = sexpr.params, paramStrings = [], hash; for(var i=0, l=params.length; i<l; i++) { @@ -104,7 +104,7 @@ PrintVisitor.prototype.sexpr = function(sexpr) { hash = sexpr.hash ? " " + this.accept(sexpr.hash) : ""; - return this.accept(sexpr.id) + " " + params + hash; + return this.accept(sexpr.path) + " " + params + hash; }; PrintVisitor.prototype.PathExpression = function(id) { @@ -113,19 +113,6 @@ PrintVisitor.prototype.PathExpression = function(id) { }; -PrintVisitor.prototype.hash = function(hash) { - var pairs = hash.pairs; - var joinedPairs = [], left, right; - - for(var i=0, l=pairs.length; i<l; i++) { - left = pairs[i][0]; - right = this.accept(pairs[i][1]); - joinedPairs.push( left + "=" + right ); - } - - return "HASH{" + joinedPairs.join(", ") + "}"; -}; - PrintVisitor.prototype.StringLiteral = function(string) { return '"' + string.value + '"'; }; @@ -137,3 +124,17 @@ PrintVisitor.prototype.NumberLiteral = function(number) { PrintVisitor.prototype.BooleanLiteral = function(bool) { return "BOOLEAN{" + bool.value + "}"; }; + +PrintVisitor.prototype.Hash = function(hash) { + var pairs = hash.pairs; + var joinedPairs = [], left, right; + + for (var i=0, l=pairs.length; i<l; i++) { + joinedPairs.push(this.accept(pairs[i])); + } + + return 'HASH{' + joinedPairs.join(', ') + '}'; +}; +PrintVisitor.prototype.HashPair = function(pair) { + return pair.key + '=' + this.accept(pair.value); +}; diff --git a/lib/handlebars/compiler/visitor.js b/lib/handlebars/compiler/visitor.js index e191bbd..c0cfab6 100644 --- a/lib/handlebars/compiler/visitor.js +++ b/lib/handlebars/compiler/visitor.js @@ -35,29 +35,32 @@ Visitor.prototype = { ContentStatement: function(content) {}, CommentStatement: function(comment) {}, - sexpr: function(sexpr) { + SubExpression: function(sexpr) { var params = sexpr.params, paramStrings = [], hash; - this.accept(sexpr.id); + this.accept(sexpr.path); for(var i=0, l=params.length; i<l; i++) { this.accept(params[i]); } this.accept(sexpr.hash); }, - PathExpression: function(id) {}, + PathExpression: function(path) {}, - hash: function(hash) { + StringLiteral: function(string) {}, + NumberLiteral: function(number) {}, + BooleanLiteral: function(bool) {}, + + Hash: function(hash) { var pairs = hash.pairs; for(var i=0, l=pairs.length; i<l; i++) { - this.accept(pairs[i][1]); + this.accept(pairs[i]); } }, - - StringLiteral: function(string) {}, - NumberLiteral: function(number) {}, - BooleanLiteral: function(bool) {} + HashPair: function(pair) { + this.accept(pair.value); + } }; export default Visitor; |