diff options
author | Kevin Decker <kpdecker@gmail.com> | 2013-12-30 16:09:41 -0800 |
---|---|---|
committer | Kevin Decker <kpdecker@gmail.com> | 2013-12-30 16:09:41 -0800 |
commit | a2ca31bb193c5c6f073cfeef023feb9cc0e98ce5 (patch) | |
tree | b96b31bd41c1a6c86f45e114b100c124f8217e6f /lib/handlebars/compiler | |
parent | ac98e7b177095be80fc5d590d15b4724dd731cab (diff) | |
parent | b09333db7946d20ba7dbc6d32d5496ab8295b8e1 (diff) | |
download | handlebars.js-a2ca31bb193c5c6f073cfeef023feb9cc0e98ce5.zip handlebars.js-a2ca31bb193c5c6f073cfeef023feb9cc0e98ce5.tar.gz handlebars.js-a2ca31bb193c5c6f073cfeef023feb9cc0e98ce5.tar.bz2 |
Merge pull request #690 from machty/subsexpr
Added support for subexpressions
Diffstat (limited to 'lib/handlebars/compiler')
-rw-r--r-- | lib/handlebars/compiler/ast.js | 24 | ||||
-rw-r--r-- | lib/handlebars/compiler/compiler.js | 90 | ||||
-rw-r--r-- | lib/handlebars/compiler/javascript-compiler.js | 42 | ||||
-rw-r--r-- | lib/handlebars/compiler/printer.js | 12 |
4 files changed, 95 insertions, 73 deletions
diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index b6c3a03..ce5ee11 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -46,7 +46,6 @@ var AST = { MustacheNode: function(rawParams, hash, open, strip, locInfo) { LocationInfo.call(this, locInfo); this.type = "mustache"; - this.hash = hash; this.strip = strip; // Open may be a string parsed from the parser or a passed boolean flag @@ -58,6 +57,25 @@ var AST = { this.escaped = !!open; } + if (rawParams instanceof AST.SexprNode) { + this.sexpr = rawParams; + } else { + // Support old AST API + this.sexpr = new AST.SexprNode(rawParams, hash); + } + + // Support old AST API that stored this info in MustacheNode + this.id = this.sexpr.id; + this.params = this.sexpr.params; + this.hash = this.sexpr.hash; + this.eligibleHelper = this.sexpr.eligibleHelper; + this.isHelper = this.sexpr.isHelper; + }, + + SexprNode: function(rawParams, hash) { + this.type = "sexpr"; + this.hash = hash; + var id = this.id = rawParams[0]; var params = this.params = rawParams.slice(1); @@ -84,8 +102,8 @@ var AST = { }, BlockNode: function(mustache, program, inverse, close, locInfo) { - if(mustache.id.original !== close.path.original) { - throw new Exception(mustache.id.original + " doesn't match " + close.path.original); + if(mustache.sexpr.id.original !== close.path.original) { + throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original); } LocationInfo.call(this, locInfo); diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js index 259c543..00353a8 100644 --- a/lib/handlebars/compiler/compiler.js +++ b/lib/handlebars/compiler/compiler.js @@ -156,12 +156,13 @@ Compiler.prototype = { inverse = this.compileProgram(inverse); } - var type = this.classifyMustache(mustache); + var sexpr = mustache.sexpr; + var type = this.classifySexpr(sexpr); if (type === "helper") { - this.helperMustache(mustache, program, inverse); + this.helperSexpr(sexpr, program, inverse); } else if (type === "simple") { - this.simpleMustache(mustache); + this.simpleSexpr(sexpr); // now that the simple mustache is resolved, we need to // evaluate it by executing `blockHelperMissing` @@ -170,7 +171,7 @@ Compiler.prototype = { this.opcode('emptyHash'); this.opcode('blockValue'); } else { - this.ambiguousMustache(mustache, program, inverse); + this.ambiguousSexpr(sexpr, program, inverse); // now that the simple mustache is resolved, we need to // evaluate it by executing `blockHelperMissing` @@ -198,6 +199,12 @@ Compiler.prototype = { } this.opcode('getContext', val.depth || 0); this.opcode('pushStringParam', val.stringModeValue, val.type); + + if (val.type === 'sexpr') { + // Subexpressions get evaluated and passed in + // in string params mode. + this.sexpr(val); + } } else { this.accept(val); } @@ -226,26 +233,17 @@ Compiler.prototype = { }, mustache: function(mustache) { - var options = this.options; - var type = this.classifyMustache(mustache); - - if (type === "simple") { - this.simpleMustache(mustache); - } else if (type === "helper") { - this.helperMustache(mustache); - } else { - this.ambiguousMustache(mustache); - } + this.sexpr(mustache.sexpr); - if(mustache.escaped && !options.noEscape) { + if(mustache.escaped && !this.options.noEscape) { this.opcode('appendEscaped'); } else { this.opcode('append'); } }, - ambiguousMustache: function(mustache, program, inverse) { - var id = mustache.id, + ambiguousSexpr: function(sexpr, program, inverse) { + var id = sexpr.id, name = id.parts[0], isBlock = program != null || inverse != null; @@ -257,8 +255,8 @@ Compiler.prototype = { this.opcode('invokeAmbiguous', name, isBlock); }, - simpleMustache: function(mustache) { - var id = mustache.id; + simpleSexpr: function(sexpr) { + var id = sexpr.id; if (id.type === 'DATA') { this.DATA(id); @@ -274,9 +272,9 @@ Compiler.prototype = { this.opcode('resolvePossibleLambda'); }, - helperMustache: function(mustache, program, inverse) { - var params = this.setupFullMustacheParams(mustache, program, inverse), - name = mustache.id.parts[0]; + helperSexpr: function(sexpr, program, inverse) { + var params = this.setupFullMustacheParams(sexpr, program, inverse), + name = sexpr.id.parts[0]; if (this.options.knownHelpers[name]) { this.opcode('invokeKnownHelper', params.length, name); @@ -287,6 +285,18 @@ Compiler.prototype = { } }, + sexpr: function(sexpr) { + var type = this.classifySexpr(sexpr); + + if (type === "simple") { + this.simpleSexpr(sexpr); + } else if (type === "helper") { + this.helperSexpr(sexpr); + } else { + this.ambiguousSexpr(sexpr); + } + }, + ID: function(id) { this.addDepth(id.depth); this.opcode('getContext', id.depth); @@ -349,14 +359,14 @@ Compiler.prototype = { } }, - classifyMustache: function(mustache) { - var isHelper = mustache.isHelper; - var isEligible = mustache.eligibleHelper; + classifySexpr: function(sexpr) { + var isHelper = sexpr.isHelper; + var isEligible = sexpr.eligibleHelper; var options = this.options; // if ambiguous, we can possibly resolve the ambiguity now if (isEligible && !isHelper) { - var name = mustache.id.parts[0]; + var name = sexpr.id.parts[0]; if (options.knownHelpers[name]) { isHelper = true; @@ -383,35 +393,27 @@ Compiler.prototype = { this.opcode('getContext', param.depth || 0); this.opcode('pushStringParam', param.stringModeValue, param.type); + + if (param.type === 'sexpr') { + // Subexpressions get evaluated and passed in + // in string params mode. + this.sexpr(param); + } } else { this[param.type](param); } } }, - setupMustacheParams: function(mustache) { - var params = mustache.params; - this.pushParams(params); - - if(mustache.hash) { - this.hash(mustache.hash); - } else { - this.opcode('emptyHash'); - } - - return params; - }, - - // this will replace setupMustacheParams when we're done - setupFullMustacheParams: function(mustache, program, inverse) { - var params = mustache.params; + setupFullMustacheParams: function(sexpr, program, inverse) { + var params = sexpr.params; this.pushParams(params); this.opcode('pushProgram', program); this.opcode('pushProgram', inverse); - if(mustache.hash) { - this.hash(mustache.hash); + if (sexpr.hash) { + this.hash(sexpr.hash); } else { this.opcode('emptyHash'); } diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 159a38b..d920d52 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -244,9 +244,6 @@ JavaScriptCompiler.prototype = { var current = this.topStack(); params.splice(1, 0, current); - // Use the options value generated from the invocation - params[params.length-1] = 'options'; - this.pushSource("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }"); }, @@ -398,10 +395,14 @@ JavaScriptCompiler.prototype = { this.pushString(type); - if (typeof string === 'string') { - this.pushString(string); - } else { - this.pushStackLiteral(string); + // If it's a subexpression, the string result + // will be pushed after this opcode. + if (type !== 'sexpr') { + if (typeof string === 'string') { + this.pushString(string); + } else { + this.pushStackLiteral(string); + } } }, @@ -409,8 +410,8 @@ JavaScriptCompiler.prototype = { this.pushStackLiteral('{}'); if (this.options.stringParams) { - this.register('hashTypes', '{}'); - this.register('hashContexts', '{}'); + this.push('{}'); // hashContexts + this.push('{}'); // hashTypes } }, pushHash: function() { @@ -421,9 +422,10 @@ JavaScriptCompiler.prototype = { this.hash = undefined; if (this.options.stringParams) { - this.register('hashContexts', '{' + hash.contexts.join(',') + '}'); - this.register('hashTypes', '{' + hash.types.join(',') + '}'); + this.push('{' + hash.contexts.join(',') + '}'); + this.push('{' + hash.types.join(',') + '}'); } + this.push('{\n ' + hash.values.join(',\n ') + '\n }'); }, @@ -526,7 +528,7 @@ JavaScriptCompiler.prototype = { invokeAmbiguous: function(name, helperCall) { this.context.aliases.functionType = '"function"'; - this.pushStackLiteral('{}'); // Hash value + this.emptyHash(); var helper = this.setupHelper(0, name, helperCall); var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); @@ -805,6 +807,11 @@ JavaScriptCompiler.prototype = { options.push("hash:" + this.popStack()); + if (this.options.stringParams) { + options.push("hashTypes:" + this.popStack()); + options.push("hashContexts:" + this.popStack()); + } + inverse = this.popStack(); program = this.popStack(); @@ -838,22 +845,13 @@ JavaScriptCompiler.prototype = { if (this.options.stringParams) { options.push("contexts:[" + contexts.join(",") + "]"); options.push("types:[" + types.join(",") + "]"); - options.push("hashContexts:hashContexts"); - options.push("hashTypes:hashTypes"); } if(this.options.data) { options.push("data:data"); } - options = "{" + options.join(",") + "}"; - if (useRegister) { - this.register('options', options); - params.push('options'); - } else { - params.push(options); - } - return params.join(", "); + params.push("{" + options.join(",") + "}"); } }; diff --git a/lib/handlebars/compiler/printer.js b/lib/handlebars/compiler/printer.js index f91ff02..ad55c7d 100644 --- a/lib/handlebars/compiler/printer.js +++ b/lib/handlebars/compiler/printer.js @@ -62,8 +62,8 @@ PrintVisitor.prototype.block = function(block) { return out; }; -PrintVisitor.prototype.mustache = function(mustache) { - var params = mustache.params, paramStrings = [], hash; +PrintVisitor.prototype.sexpr = function(sexpr) { + var params = sexpr.params, paramStrings = [], hash; for(var i=0, l=params.length; i<l; i++) { paramStrings.push(this.accept(params[i])); @@ -71,9 +71,13 @@ PrintVisitor.prototype.mustache = function(mustache) { params = "[" + paramStrings.join(", ") + "]"; - hash = mustache.hash ? " " + this.accept(mustache.hash) : ""; + hash = sexpr.hash ? " " + this.accept(sexpr.hash) : ""; + + return this.accept(sexpr.id) + " " + params + hash; +}; - return this.pad("{{ " + this.accept(mustache.id) + " " + params + hash + " }}"); +PrintVisitor.prototype.mustache = function(mustache) { + return this.pad("{{ " + this.accept(mustache.sexpr) + " }}"); }; PrintVisitor.prototype.partial = function(partial) { |