diff options
author | kpdecker <kpdecker@gmail.com> | 2014-08-23 16:29:50 -0500 |
---|---|---|
committer | kpdecker <kpdecker@gmail.com> | 2014-08-23 16:29:50 -0500 |
commit | 35b196d069d0cf9e7ecba9f9c996c2112b3973a2 (patch) | |
tree | 9b4af5981298bccc8e90d57b66773da7b7a1543f /lib/handlebars/compiler | |
parent | 3531e041174509c6c3c69417b1714dcda6bccb3e (diff) | |
parent | 529e2b67960dba2e41122fc4d56b5c2af5ada9a5 (diff) | |
download | handlebars.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.js | 178 | ||||
-rw-r--r-- | lib/handlebars/compiler/base.js | 10 | ||||
-rw-r--r-- | lib/handlebars/compiler/helpers.js | 163 |
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; + } +} |