summaryrefslogtreecommitdiffstats
path: root/lib/handlebars/compiler
diff options
context:
space:
mode:
authorKevin Decker <kpdecker@gmail.com>2013-12-30 16:09:41 -0800
committerKevin Decker <kpdecker@gmail.com>2013-12-30 16:09:41 -0800
commita2ca31bb193c5c6f073cfeef023feb9cc0e98ce5 (patch)
treeb96b31bd41c1a6c86f45e114b100c124f8217e6f /lib/handlebars/compiler
parentac98e7b177095be80fc5d590d15b4724dd731cab (diff)
parentb09333db7946d20ba7dbc6d32d5496ab8295b8e1 (diff)
downloadhandlebars.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.js24
-rw-r--r--lib/handlebars/compiler/compiler.js90
-rw-r--r--lib/handlebars/compiler/javascript-compiler.js42
-rw-r--r--lib/handlebars/compiler/printer.js12
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) {