summaryrefslogtreecommitdiffstats
path: root/lib/handlebars/compiler
diff options
context:
space:
mode:
authorkpdecker <kpdecker@gmail.com>2014-08-23 16:29:50 -0500
committerkpdecker <kpdecker@gmail.com>2014-08-23 16:29:50 -0500
commit35b196d069d0cf9e7ecba9f9c996c2112b3973a2 (patch)
tree9b4af5981298bccc8e90d57b66773da7b7a1543f /lib/handlebars/compiler
parent3531e041174509c6c3c69417b1714dcda6bccb3e (diff)
parent529e2b67960dba2e41122fc4d56b5c2af5ada9a5 (diff)
downloadhandlebars.js-35b196d069d0cf9e7ecba9f9c996c2112b3973a2.zip
handlebars.js-35b196d069d0cf9e7ecba9f9c996c2112b3973a2.tar.gz
handlebars.js-35b196d069d0cf9e7ecba9f9c996c2112b3973a2.tar.bz2
Merge branch 'mmun-refactor-parser'
Diffstat (limited to 'lib/handlebars/compiler')
-rw-r--r--lib/handlebars/compiler/ast.js178
-rw-r--r--lib/handlebars/compiler/base.js10
-rw-r--r--lib/handlebars/compiler/helpers.js163
3 files changed, 177 insertions, 174 deletions
diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js
index ae5ea63..5a47e2b 100644
--- a/lib/handlebars/compiler/ast.js
+++ b/lib/handlebars/compiler/ast.js
@@ -1,6 +1,6 @@
import Exception from "../exception";
-function LocationInfo(locInfo){
+function LocationInfo(locInfo) {
locInfo = locInfo || {};
this.firstLine = locInfo.first_line;
this.firstColumn = locInfo.first_column;
@@ -9,41 +9,11 @@ function LocationInfo(locInfo){
}
var AST = {
- ProgramNode: function(isRoot, statements, inverseStrip, inverse, locInfo) {
- var inverseLocationInfo, firstInverseNode;
- if (arguments.length === 4) {
- locInfo = inverse;
- inverse = null;
- } else if (arguments.length === 3) {
- locInfo = inverseStrip;
- inverseStrip = null;
- }
-
+ ProgramNode: function(statements, strip, locInfo) {
LocationInfo.call(this, locInfo);
this.type = "program";
this.statements = statements;
- this.strip = {};
-
- if(inverse) {
- firstInverseNode = inverse[0];
- if (firstInverseNode) {
- inverseLocationInfo = {
- first_line: firstInverseNode.firstLine,
- last_line: firstInverseNode.lastLine,
- last_column: firstInverseNode.lastColumn,
- first_column: firstInverseNode.firstColumn
- };
- this.inverse = new AST.ProgramNode(isRoot, inverse, inverseStrip, inverseLocationInfo);
- } else {
- this.inverse = new AST.ProgramNode(isRoot, inverse, inverseStrip);
- }
- this.strip.right = inverseStrip.left;
- } else if (inverseStrip) {
- this.strip.left = inverseStrip.right;
- }
-
- // Scan all children to complete the standalone analysis
- checkStandalone(this, isRoot, statements);
+ this.strip = strip;
},
MustacheNode: function(rawParams, hash, open, strip, locInfo) {
@@ -111,43 +81,14 @@ var AST = {
this.strip.inlineStandalone = true;
},
- BlockNode: function(mustache, program, inverse, close, locInfo) {
+ BlockNode: function(mustache, program, inverse, strip, locInfo) {
LocationInfo.call(this, locInfo);
- if(mustache.sexpr.id.original !== close.path.original) {
- throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original, this);
- }
-
this.type = 'block';
this.mustache = mustache;
this.program = program;
this.inverse = inverse;
-
- var firstChild = program || inverse,
- lastChild = inverse || program;
-
- this.strip = {
- left: mustache.strip.left,
- right: close.strip.right,
-
- // Determine the standalone candiacy. Basically flag our content as being possibly standalone
- // so our parent can determine if we actually are standalone
- openStandalone: isNextWhitespace(firstChild),
- closeStandalone: isPrevWhitespace(lastChild)
- };
-
- // Calculate stripping for any else statements
- firstChild.strip.left = mustache.strip.right;
- lastChild.strip.right = close.strip.left;
-
- // Find standalone else statments
- if (program && inverse
- && isPrevWhitespace(program)
- && isNextWhitespace(inverse)) {
-
- omitLeft(program);
- omitRight(inverse);
- }
+ this.strip = strip;
if (inverse && !program) {
this.isInverse = true;
@@ -165,7 +106,7 @@ var AST = {
this.type = 'block';
this.mustache = mustache;
- this.program = new AST.ProgramNode(false, [content], locInfo);
+ this.program = new AST.ProgramNode([content], {}, locInfo);
},
ContentNode: function(string, locInfo) {
@@ -269,113 +210,6 @@ var AST = {
};
-function checkStandalone(program, isRoot, statements) {
- for (var i = 0, l = statements.length; i < l; i++) {
- var current = statements[i],
- strip = current.strip;
-
- if (!strip) {
- continue;
- }
-
- var _isPrevWhitespace = isPrevWhitespace(program, i, isRoot, current.type === 'partial'),
- _isNextWhitespace = isNextWhitespace(program, i, isRoot);
- strip.openStandalone = strip.openStandalone && _isPrevWhitespace;
- strip.closeStandalone = strip.closeStandalone && _isNextWhitespace;
- strip.inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
-
- if (strip.inlineStandalone) {
- omitRight(program, i);
-
- if (omitLeft(program, i)) {
- // If we are on a standalone node, save the indent info for partials
- if (current.type === 'partial') {
- current.indent = statements[i-1].string;
- }
- }
- }
- if (strip.openStandalone) {
- omitRight(current.program || current.inverse);
-
- // Strip out the previous content node if it's whitespace only
- omitLeft(program, i);
- }
- if (strip.closeStandalone) {
- // Always strip the next node
- omitRight(program, i);
-
- omitLeft(current.inverse || current.program);
- }
- }
-}
-function isPrevWhitespace(parent, i, isRoot, disallowIndent) {
- var statements = parent.statements;
- if (i === undefined) {
- i = statements.length;
- }
-
- // Nodes that end with newlines are considered whitespace (but are special
- // cased for strip operations)
- var prev = statements[i-1];
- if (prev && /\n$/.test(prev.string)) {
- return true;
- }
-
- return checkWhitespace(isRoot, prev, statements[i-2]);
-}
-function isNextWhitespace(parent, i, isRoot) {
- var statements = parent.statements;
- if (i === undefined) {
- i = -1;
- }
-
- return checkWhitespace(isRoot, statements[i+1], statements[i+2]);
-}
-function checkWhitespace(isRoot, next1, next2, disallowIndent) {
- if (!next1) {
- return isRoot;
- } else if (next1.type === 'content') {
- // Check if the previous node is empty or whitespace only
- if (disallowIndent ? !next1.string : /^[\s]*$/.test(next1.string)) {
- if (next2) {
- return next2.type === 'content' || /\n$/.test(next1.string);
- } else {
- return isRoot || (next1.string.indexOf('\n') >= 0);
- }
- }
- }
-}
-
-// Marks the node to the right of the position as omitted.
-// I.e. " "{{foo}} will mark the " " node as omitted.
-//
-// If i is undefined, then the first child will be marked as such.
-function omitRight(program, i) {
- var first = program.statements[i == null ? 0 : i + 1];
- if (first) {
- first.omit = true;
- }
-}
-
-// Marks the node to the left of the position as omitted.
-// I.e. " "{{foo}} will mark the " " node as omitted.
-//
-// If i is undefined then the last child will be marked as such.
-function omitLeft(program, i) {
- var statements = program.statements;
- if (i === undefined) {
- i = statements.length;
- }
-
- var last = statements[i-1],
- prev = statements[i-2];
-
- // We omit the last node if it's whitespace only and not preceeded by a non-content node.
- if (last && /^[\s]*$/.test(last.string) && (!prev || prev.type === 'content')) {
- return last.omit = true;
- }
-}
-
// Must be exported as an object rather than the root of the module as the jison lexer
// most modify the object to operate properly.
export default AST;
diff --git a/lib/handlebars/compiler/base.js b/lib/handlebars/compiler/base.js
index 722f09a..1378463 100644
--- a/lib/handlebars/compiler/base.js
+++ b/lib/handlebars/compiler/base.js
@@ -1,12 +1,18 @@
import parser from "./parser";
import AST from "./ast";
+module Helpers from "./helpers";
+import { extend } from "../utils";
export { parser };
+var yy = {};
+extend(yy, Helpers, AST);
+
export function parse(input) {
// Just return if an already-compile AST was passed in.
- if(input.constructor === AST.ProgramNode) { return input; }
+ if (input.constructor === AST.ProgramNode) { return input; }
+
+ parser.yy = yy;
- parser.yy = AST;
return parser.parse(input);
}
diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js
new file mode 100644
index 0000000..5d8fec1
--- /dev/null
+++ b/lib/handlebars/compiler/helpers.js
@@ -0,0 +1,163 @@
+import Exception from "../exception";
+
+export function stripFlags(open, close) {
+ return {
+ left: open.charAt(2) === '~',
+ right: close.charAt(close.length-3) === '~'
+ };
+}
+
+
+export function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) {
+ /*jshint -W040 */
+ if (mustache.sexpr.id.original !== close.path.original) {
+ throw new Exception(mustache.sexpr.id.original + ' doesn\'t match ' + close.path.original, mustache);
+ }
+
+ var inverse = inverseAndProgram && inverseAndProgram.program;
+
+ var strip = {
+ left: mustache.strip.left,
+ right: close.strip.right,
+
+ // Determine the standalone candiacy. Basically flag our content as being possibly standalone
+ // so our parent can determine if we actually are standalone
+ openStandalone: isNextWhitespace(program.statements),
+ closeStandalone: isPrevWhitespace((inverse || program).statements)
+ };
+
+ if (inverse) {
+ var inverseStrip = inverseAndProgram.strip;
+
+ program.strip.left = mustache.strip.right;
+ program.strip.right = inverseStrip.left;
+ inverse.strip.left = inverseStrip.right;
+ inverse.strip.right = close.strip.left;
+
+ // Find standalone else statments
+ if (isPrevWhitespace(program.statements)
+ && isNextWhitespace(inverse.statements)) {
+
+ omitLeft(program.statements);
+ omitRight(inverse.statements);
+ }
+ } else {
+ program.strip.left = mustache.strip.right;
+ program.strip.right = close.strip.left;
+ }
+
+ if (inverted) {
+ return new this.BlockNode(mustache, inverse, program, strip, locInfo);
+ } else {
+ return new this.BlockNode(mustache, program, inverse, strip, locInfo);
+ }
+}
+
+
+export function prepareProgram(statements, isRoot) {
+ for (var i = 0, l = statements.length; i < l; i++) {
+ var current = statements[i],
+ strip = current.strip;
+
+ if (!strip) {
+ continue;
+ }
+
+ var _isPrevWhitespace = isPrevWhitespace(statements, i, isRoot, current.type === 'partial'),
+ _isNextWhitespace = isNextWhitespace(statements, i, isRoot),
+
+ openStandalone = strip.openStandalone && _isPrevWhitespace,
+ closeStandalone = strip.closeStandalone && _isNextWhitespace,
+ inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace;
+
+ if (inlineStandalone) {
+ omitRight(statements, i);
+
+ if (omitLeft(statements, i)) {
+ // If we are on a standalone node, save the indent info for partials
+ if (current.type === 'partial') {
+ current.indent = statements[i-1].original;
+ }
+ }
+ }
+ if (openStandalone) {
+ omitRight((current.program || current.inverse).statements);
+
+ // Strip out the previous content node if it's whitespace only
+ omitLeft(statements, i);
+ }
+ if (closeStandalone) {
+ // Always strip the next node
+ omitRight(statements, i);
+
+ omitLeft((current.inverse || current.program).statements);
+ }
+ }
+
+ return statements;
+}
+
+function isPrevWhitespace(statements, i, isRoot, disallowIndent) {
+ if (i === undefined) {
+ i = statements.length;
+ }
+
+ // Nodes that end with newlines are considered whitespace (but are special
+ // cased for strip operations)
+ var prev = statements[i-1];
+ if (prev && /\n$/.test(prev.string)) {
+ return true;
+ }
+
+ return checkWhitespace(isRoot, prev, statements[i-2]);
+}
+function isNextWhitespace(statements, i, isRoot) {
+ if (i === undefined) {
+ i = -1;
+ }
+
+ return checkWhitespace(isRoot, statements[i+1], statements[i+2]);
+}
+function checkWhitespace(isRoot, next1, next2, disallowIndent) {
+ if (!next1) {
+ return isRoot;
+ } else if (next1.type === 'content') {
+ // Check if the previous node is empty or whitespace only
+ if (disallowIndent ? !next1.string : /^[\s]*$/.test(next1.string)) {
+ if (next2) {
+ return next2.type === 'content' || /\n$/.test(next1.string);
+ } else {
+ return isRoot || (next1.string.indexOf('\n') >= 0);
+ }
+ }
+ }
+}
+
+// Marks the node to the right of the position as omitted.
+// I.e. {{foo}}' ' will mark the ' ' node as omitted.
+//
+// If i is undefined, then the first child will be marked as such.
+function omitRight(statements, i) {
+ var first = statements[i == null ? 0 : i + 1];
+ if (first) {
+ first.omit = true;
+ }
+}
+
+// Marks the node to the left of the position as omitted.
+// I.e. ' '{{foo}} will mark the ' ' node as omitted.
+//
+// If i is undefined then the last child will be marked as such.
+function omitLeft(statements, i) {
+ if (i === undefined) {
+ i = statements.length;
+ }
+
+ var last = statements[i-1],
+ prev = statements[i-2];
+
+ // We omit the last node if it's whitespace only and not preceeded by a non-content node.
+ if (last && /^[\s]*$/.test(last.string) && (!prev || prev.type === 'content')) {
+ return last.omit = true;
+ }
+}