summaryrefslogtreecommitdiffstats
path: root/lib/handlebars/compiler/compiler.js
diff options
context:
space:
mode:
authortomhuda <tomhuda@tilde.io>2012-05-28 17:35:21 -0700
committertomhuda <tomhuda@tilde.io>2012-05-28 17:35:21 -0700
commit727eb26cb6a9e89ab08596dedbabcec0becb4d75 (patch)
tree1e9d19609bc1088196cd3646d1746cf427ab120d /lib/handlebars/compiler/compiler.js
parent175c6fae0f704b2f1088728136e238186fbb1cf3 (diff)
downloadhandlebars.js-727eb26cb6a9e89ab08596dedbabcec0becb4d75.zip
handlebars.js-727eb26cb6a9e89ab08596dedbabcec0becb4d75.tar.gz
handlebars.js-727eb26cb6a9e89ab08596dedbabcec0becb4d75.tar.bz2
Remove unneeded code and add docs
There were a few operations that are no longer needed, so remove them. Also document all operations.
Diffstat (limited to 'lib/handlebars/compiler/compiler.js')
-rw-r--r--lib/handlebars/compiler/compiler.js382
1 files changed, 213 insertions, 169 deletions
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index 74b0b0b..cab5b30 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -240,21 +240,7 @@ Handlebars.JavaScriptCompiler = function() {};
ID: function(id) {
this.addDepth(id.depth);
this.opcode('getContext', id.depth);
-
- var name = id.parts[0];
-
- if (!name) {
- this.opcode('pushContext');
- } else if (!id.isScoped && this.options.knownHelpers[name]) {
- this.declare('usingKnownHelper', true);
- this.opcode('knownHelper', name);
- } else if (id.isScoped || this.options.knownHelpersOnly) {
- this.declare('usingKnownHelper', false);
- this.opcode('lookupOnContext', name);
- } else {
- this.declare('usingKnownHelper', false);
- this.opcode('lookupWithHelpers', name);
- }
+ this.opcode('lookupOnContext', id.parts[0]);
for(var i=1, l=id.parts.length; i<l; i++) {
this.opcode('lookup', id.parts[i]);
@@ -361,14 +347,6 @@ Handlebars.JavaScriptCompiler = function() {};
}
return params;
- },
-
- setupStackForMustache: function(mustache) {
- var params = this.setupMustacheParams(mustache);
-
- this.ID(mustache.id);
-
- return params;
}
};
@@ -525,6 +503,15 @@ Handlebars.JavaScriptCompiler = function() {};
}
},
+ // [blockValue]
+ //
+ // On stack, before: hash, inverse, program, value
+ // On stack, after: return value of blockHelperMissing
+ //
+ // The purpose of this opcode is to take a block of the form
+ // `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
+ // replace it on the stack with the result of properly
+ // invoking blockHelperMissing.
blockValue: function() {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
@@ -537,6 +524,12 @@ Handlebars.JavaScriptCompiler = function() {};
});
},
+ // [ambiguousBlockValue]
+ //
+ // On stack, before: hash, inverse, program, value
+ // Compiler value, before: lastHelper=value of last found helper, if any
+ // On stack, after, if no lastHelper: same as [blockValue]
+ // On stack, after, if lastHelper: value
ambiguousBlockValue: function() {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
@@ -549,10 +542,25 @@ Handlebars.JavaScriptCompiler = function() {};
this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
},
+ // [appendContent]
+ //
+ // On stack, before: ...
+ // On stack, after: ...
+ //
+ // Appends the string value of `content` to the current buffer
appendContent: function(content) {
this.source.push(this.appendToBuffer(this.quotedString(content)));
},
+ // [append]
+ //
+ // On stack, before: value, ...
+ // On stack, after: ...
+ //
+ // Coerces `value` to a String and appends it to the current buffer.
+ //
+ // If `value` is truthy, or 0, it is coerced into a string and appended
+ // Otherwise, the empty string is appended
append: function() {
var local = this.popStack();
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
@@ -561,6 +569,12 @@ Handlebars.JavaScriptCompiler = function() {};
}
},
+ // [appendEscaped]
+ //
+ // On stack, before: value, ...
+ // On stack, after: ...
+ //
+ // Escape `value` and append it to the buffer
appendEscaped: function() {
var opcode = this.nextOpcode(), extra = "";
this.context.aliases.escapeExpression = 'this.escapeExpression';
@@ -573,24 +587,47 @@ Handlebars.JavaScriptCompiler = function() {};
this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")" + extra));
},
+ // [getContext]
+ //
+ // On stack, before: ...
+ // On stack, after: ...
+ // Compiler value, after: lastContext=depth
+ //
+ // Set the value of the `lastContext` compiler value to the depth
getContext: function(depth) {
if(this.lastContext !== depth) {
this.lastContext = depth;
}
},
- knownHelper: function(name) {
- this.pushStackLiteral(this.nameLookup('helpers', name, 'helper'));
- },
-
+ // [lookupOnContext]
+ //
+ // On stack, before: ...
+ // On stack, after: currentContext[name], ...
+ //
+ // Looks up the value of `name` on the current context and pushes
+ // it onto the stack.
lookupOnContext: function(name) {
this.pushStack(this.nameLookup('depth' + this.lastContext, name, 'context'));
},
+ // [pushContext]
+ //
+ // On stack, before: ...
+ // On stack, after: currentContext, ...
+ //
+ // Pushes the value of the current context onto the stack.
pushContext: function() {
this.pushStackLiteral('depth' + this.lastContext);
},
+ // [resolvePossibleLambda]
+ //
+ // On stack, before: value, ...
+ // On stack, after: resolved value, ...
+ //
+ // If the `value` is a lambda, replace it on the stack by
+ // the return value of the lambda
resolvePossibleLambda: function() {
this.context.aliases.functionType = '"function"';
@@ -599,57 +636,72 @@ Handlebars.JavaScriptCompiler = function() {};
});
},
- lookupWithHelpers: function(name) {
- var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
- this.register('foundHelper', helperName);
- this.pushStack("foundHelper || " + this.nameLookup('depth' + this.lastContext, name, 'context'));
- },
-
+ // [lookup]
+ //
+ // On stack, before: value, ...
+ // On stack, after: value[name], ...
+ //
+ // Replace the value on the stack with the result of looking
+ // up `name` on `value`
lookup: function(name) {
this.replaceStack(function(current) {
return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
});
},
+ // [pushStringParam]
+ //
+ // On stack, before: ...
+ // On stack, after: string, currentContext, ...
+ //
+ // This opcode is designed for use in string mode, which
+ // provides the string value of a parameter along with its
+ // depth rather than resolving it immediately.
pushStringParam: function(string) {
this.pushStackLiteral('depth' + this.lastContext);
this.pushString(string);
},
+ // [pushString]
+ //
+ // On stack, before: ...
+ // On stack, after: quotedString(string), ...
+ //
+ // Push a quoted version of `string` onto the stack
pushString: function(string) {
this.pushStackLiteral(this.quotedString(string));
},
- push: function(name) {
- this.pushStack(name);
+ // [push]
+ //
+ // On stack, before: ...
+ // On stack, after: expr, ...
+ //
+ // Push an expression onto the stack
+ push: function(expr) {
+ this.pushStack(expr);
},
+ // [pushLiteral]
+ //
+ // On stack, before: ...
+ // On stack, after: value, ...
+ //
+ // Pushes a value onto the stack. This operation prevents
+ // the compiler from creating a temporary variable to hold
+ // it.
pushLiteral: function(value) {
this.pushStackLiteral(value);
},
- // The rules for mustaches containing a block are:
+ // [pushProgram]
//
- // If the first parameter resolves to a function, call the function with the remaining parameters
- // Otherwise, invoke blockHelperMissing with the string form of the first parameter and the
- // remaining parameters.
- invokeProgram: function(guid, paramSize) {
- var options = [], program, inverse;
-
- if (inverse = this.programExpression(this.inverse)) {
- options.push("inverse:" + inverse);
- }
-
- if (program = this.programExpression(guid)) {
- options.push("fn:" + program);
- }
-
- this.populateParams(paramSize, null, options, false, function(nextStack, helperMissingString) {
- this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
- this.source.push("else { " + nextStack + " = blockHelperMissing.call(" + helperMissingString + "); }");
- });
- },
-
+ // On stack, before: ...
+ // On stack, after: program(guid), ...
+ //
+ // Push a program expression onto the stack. This takes
+ // a compile-time guid and converts it into a runtime-accessible
+ // expression.
pushProgram: function(guid) {
if (guid != null) {
this.pushStackLiteral(this.programExpression(guid));
@@ -658,6 +710,15 @@ Handlebars.JavaScriptCompiler = function() {};
}
},
+ // [invokeHelper]
+ //
+ // On stack, before: hash, inverse, program, params..., ...
+ // On stack, after: result of helper invocation
+ //
+ // Pops off the helper's parameters, invokes the helper,
+ // and pushes the helper's return value onto the stack.
+ //
+ // If the helper is not found, `helperMissing` is called.
invokeHelper: function(paramSize, name) {
this.context.aliases.helperMissing = 'helpers.helperMissing';
@@ -669,11 +730,30 @@ Handlebars.JavaScriptCompiler = function() {};
helper.helperMissingParams + ")");
},
+ // [invokeKnownHelper]
+ //
+ // On stack, before: hash, inverse, program, params..., ...
+ // On stack, after: result of helper invocation
+ //
+ // This operation is used when the helper is known to exist,
+ // so a `helperMissing` fallback is not required.
invokeKnownHelper: function(paramSize, name) {
var helper = this.setupHelper(paramSize, name);
this.pushStack(helper.name + ".call(" + helper.callParams + ")");
},
+ // [invokeAmbiguous]
+ //
+ // On stack, before: hash, inverse, program, params..., ...
+ // On stack, after: result of disambiguation
+ //
+ // This operation is used when an expression like `{{foo}}`
+ // is provided, but we don't know at compile-time whether it
+ // is a helper or a path.
+ //
+ // This operation emits more code than the other options,
+ // and can be avoided by passing the `knownHelpers` and
+ // `knownHelpersOnly` flags at compile-time.
invokeAmbiguous: function(name) {
this.context.aliases.functionType = '"function"';
@@ -690,119 +770,15 @@ Handlebars.JavaScriptCompiler = function() {};
this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '() : ' + nextStack + '; }');
},
- setupHelper: function(paramSize, name) {
- var params = [];
- this.setupParams(paramSize, params);
- var foundHelper = this.nameLookup('helpers', name, 'helper');
-
- return {
- params: params,
- name: foundHelper,
- callParams: ["depth0"].concat(params).join(", "),
- helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
- };
- },
-
- // the params and contexts arguments are passed in arrays
- // to fill in
- setupParams: function(paramSize, params) {
- var options = [], contexts = [], param, inverse, program;
-
- options.push("hash:" + this.popStack());
-
- inverse = this.popStack();
- program = this.popStack();
-
- // Avoid setting fn and inverse if neither are set. This allows
- // helpers to do a check for `if (options.fn)`
- if (program || inverse) {
- if (!program) {
- this.context.aliases.self = "this";
- program = "self.noop";
- }
-
- if (!inverse) {
- this.context.aliases.self = "this";
- inverse = "self.noop";
- }
-
- options.push("inverse:" + inverse);
- options.push("fn:" + program);
- }
-
- for(var i=0; i<paramSize; i++) {
- param = this.popStack();
- params.push(param);
-
- if(this.options.stringParams) {
- contexts.push(this.popStack());
- }
- }
-
- if (this.options.stringParams) {
- options.push("contexts:[" + contexts.join(",") + "]");
- }
-
- if(this.options.data) {
- options.push("data:data");
- }
-
- params.push("{" + options.join(",") + "}");
- return params.join(", ");
- },
-
- populateParams: function(paramSize, helperId, options, resolveLambdas, callback) {
- var id = this.popStack(), nextStack;
- var params = [], contexts = [], param, paramString, stringOptions;
-
- options.push("hash:" + this.popStack());
-
- for(var i=0; i<paramSize; i++) {
- param = this.popStack();
- params.push(param);
-
- if(this.options.stringParams) {
- contexts.push(this.popStack());
- }
- }
-
- if (this.options.stringParams) {
- options.push("contexts:[" + contexts.join(",") + "]");
- }
-
- if(this.options.data) {
- options.push("data:data");
- }
-
- this.register("params", "{" + options.join(",") + "}");
-
- params.push("params");
-
- if (this.usingKnownHelper) {
- this.pushStack(id + ".call(" + ["depth0"].concat(params).join(", ") + ")");
- } else {
- this.findAndCallHelper(params, id, helperId || id, resolveLambdas, callback);
- }
- },
-
- findAndCallHelper: function(params, id, helperId, resolveLambdas, callback) {
- this.useRegister('foundHelper');
- this.context.aliases.functionType = '"function"';
-
- var paramString = ["depth0"].concat(params).join(", "), nextStack;
- var helperMissingString = ["depth0"].concat(helperId).concat(params).join(", ");
-
- nextStack = this.nextStack();
- var condition = resolveLambdas ? "" : "foundHelper && ";
-
- this.source.push("if(" + condition + "typeof " + id + " === functionType) { " +
- nextStack + " = " + id + ".call(" + paramString + "); }");
-
- callback.call(this, nextStack, helperMissingString, id);
- },
-
- invokePartial: function(context) {
- var params = [this.nameLookup('partials', context, 'partial'), "'" + context + "'", this.popStack(), "helpers", "partials"];
+ // [invokePartial]
+ //
+ // On stack, before: context, ...
+ // On stack after: result of partial invocation
+ //
+ // This operation pops off a context, invokes a partial with that context,
+ // and pushes the result of the invocation back.
+ invokePartial: function(name) {
+ var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
if (this.options.data) {
params.push("data");
@@ -812,6 +788,13 @@ Handlebars.JavaScriptCompiler = function() {};
this.pushStack("self.invokePartial(" + params.join(", ") + ");");
},
+ // [assignToHash]
+ //
+ // On stack, before: value, hash, ...
+ // On stack, after: hash, ...
+ //
+ // Pops a value and hash off the stack, assigns `hash[key] = value`
+ // and pushes the hash back onto the stack.
assignToHash: function(key) {
var value = this.popStack();
var hash = this.topStack();
@@ -934,6 +917,67 @@ Handlebars.JavaScriptCompiler = function() {};
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r') + '"';
+ },
+
+ setupHelper: function(paramSize, name) {
+ var params = [];
+ this.setupParams(paramSize, params);
+ var foundHelper = this.nameLookup('helpers', name, 'helper');
+
+ return {
+ params: params,
+ name: foundHelper,
+ callParams: ["depth0"].concat(params).join(", "),
+ helperMissingParams: ["depth0", this.quotedString(name)].concat(params).join(", ")
+ };
+ },
+
+ // the params and contexts arguments are passed in arrays
+ // to fill in
+ setupParams: function(paramSize, params) {
+ var options = [], contexts = [], param, inverse, program;
+
+ options.push("hash:" + this.popStack());
+
+ inverse = this.popStack();
+ program = this.popStack();
+
+ // Avoid setting fn and inverse if neither are set. This allows
+ // helpers to do a check for `if (options.fn)`
+ if (program || inverse) {
+ if (!program) {
+ this.context.aliases.self = "this";
+ program = "self.noop";
+ }
+
+ if (!inverse) {
+ this.context.aliases.self = "this";
+ inverse = "self.noop";
+ }
+
+ options.push("inverse:" + inverse);
+ options.push("fn:" + program);
+ }
+
+ for(var i=0; i<paramSize; i++) {
+ param = this.popStack();
+ params.push(param);
+
+ if(this.options.stringParams) {
+ contexts.push(this.popStack());
+ }
+ }
+
+ if (this.options.stringParams) {
+ options.push("contexts:[" + contexts.join(",") + "]");
+ }
+
+ if(this.options.data) {
+ options.push("data:data");
+ }
+
+ params.push("{" + options.join(",") + "}");
+ return params.join(", ");
}
};