From 12e3d210e8b3b22d19823e59bb789dc8768eb34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mu=C3=B1oz?= Date: Sun, 2 Nov 2014 14:54:54 -0500 Subject: [PATCH 1/2] Simplify BlockNode by removing intermediate MustacheNode --- lib/handlebars/compiler/ast.js | 14 ++++--------- lib/handlebars/compiler/compiler.js | 3 +-- lib/handlebars/compiler/helpers.js | 32 ++++++++++++++++++++++------- lib/handlebars/compiler/printer.js | 2 +- spec/ast.js | 2 +- spec/parser.js | 22 ++++++++++---------- src/handlebars.yy | 14 ++++++++----- 7 files changed, 52 insertions(+), 37 deletions(-) diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index e05ceec63..05de762b6 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -79,11 +79,11 @@ var AST = { this.strip.inlineStandalone = true; }, - BlockNode: function(mustache, program, inverse, strip, locInfo) { + BlockNode: function(sexpr, program, inverse, strip, locInfo) { LocationInfo.call(this, locInfo); this.type = 'block'; - this.mustache = mustache; + this.sexpr = sexpr; this.program = program; this.inverse = inverse; this.strip = strip; @@ -93,17 +93,11 @@ var AST = { } }, - RawBlockNode: function(mustache, content, close, locInfo) { + RawBlockNode: function(sexpr, content, locInfo) { LocationInfo.call(this, locInfo); - if (mustache.sexpr.id.original !== close) { - throw new Exception(mustache.sexpr.id.original + " doesn't match " + close, this); - } - - content = new AST.ContentNode(content, locInfo); - this.type = 'block'; - this.mustache = mustache; + this.sexpr = sexpr; this.program = new AST.ProgramNode([content], {}, locInfo); }, diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js index 1303d8fc4..51e66203a 100644 --- a/lib/handlebars/compiler/compiler.js +++ b/lib/handlebars/compiler/compiler.js @@ -108,7 +108,7 @@ Compiler.prototype = { }, block: function(block) { - var mustache = block.mustache, + var sexpr = block.sexpr, program = block.program, inverse = block.inverse; @@ -120,7 +120,6 @@ Compiler.prototype = { inverse = this.compileProgram(inverse); } - var sexpr = mustache.sexpr; var type = this.classifySexpr(sexpr); if (type === "helper") { diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js index 20f13d656..c9a35c24d 100644 --- a/lib/handlebars/compiler/helpers.js +++ b/lib/handlebars/compiler/helpers.js @@ -12,17 +12,35 @@ export function stripComment(comment) { .replace(/-?-?~?\}\}$/, ''); } +export function prepareRawBlock(openRawBlock, content, close, locInfo) { + /*jshint -W040 */ + if (openRawBlock.sexpr.id.original !== close) { + var errorNode = { + firstLine: openRawBlock.sexpr.firstLine, + firstColumn: openRawBlock.sexpr.firstColumn + }; + + throw new Exception(openRawBlock.sexpr.id.original + " doesn't match " + close, errorNode); + } -export function prepareBlock(mustache, program, inverseAndProgram, close, inverted, locInfo) { + return new this.RawBlockNode(openRawBlock.sexpr, content, locInfo); +} + +export function prepareBlock(openBlock, 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); + if (openBlock.sexpr.id.original !== close.path.original) { + var errorNode = { + firstLine: openBlock.sexpr.firstLine, + firstColumn: openBlock.sexpr.firstColumn + }; + + throw new Exception(openBlock.sexpr.id.original + ' doesn\'t match ' + close.path.original, errorNode); } var inverse = inverseAndProgram && inverseAndProgram.program; var strip = { - left: mustache.strip.left, + left: openBlock.strip.left, right: close.strip.right, // Determine the standalone candiacy. Basically flag our content as being possibly standalone @@ -31,7 +49,7 @@ export function prepareBlock(mustache, program, inverseAndProgram, close, invert closeStandalone: isPrevWhitespace((inverse || program).statements) }; - if (mustache.strip.right) { + if (openBlock.strip.right) { omitRight(program.statements, null, true); } @@ -62,9 +80,9 @@ export function prepareBlock(mustache, program, inverseAndProgram, close, invert } if (inverted) { - return new this.BlockNode(mustache, inverse, program, strip, locInfo); + return new this.BlockNode(openBlock.sexpr, inverse, program, strip, locInfo); } else { - return new this.BlockNode(mustache, program, inverse, strip, locInfo); + return new this.BlockNode(openBlock.sexpr, program, inverse, strip, locInfo); } } diff --git a/lib/handlebars/compiler/printer.js b/lib/handlebars/compiler/printer.js index 7654245c5..c329373ea 100644 --- a/lib/handlebars/compiler/printer.js +++ b/lib/handlebars/compiler/printer.js @@ -40,7 +40,7 @@ PrintVisitor.prototype.block = function(block) { out = out + this.pad("BLOCK:"); this.padding++; - out = out + this.accept(block.mustache); + out = out + this.pad(this.accept(block.sexpr)); if (block.program) { out = out + this.pad("PROGRAM:"); this.padding++; diff --git a/spec/ast.js b/spec/ast.js index 7118c72dd..9b3edb03b 100644 --- a/spec/ast.js +++ b/spec/ast.js @@ -71,7 +71,7 @@ describe('ast', function() { it('should throw on mustache mismatch', function() { shouldThrow(function() { handlebarsEnv.parse("\n {{#foo}}{{/bar}}"); - }, Handlebars.Exception, "foo doesn't match bar - 2:2"); + }, Handlebars.Exception, "foo doesn't match bar - 2:5"); }); it('stores location info', function(){ diff --git a/spec/parser.js b/spec/parser.js index 131160a74..3d5fe0e09 100644 --- a/spec/parser.js +++ b/spec/parser.js @@ -109,46 +109,46 @@ describe('parser', function() { }); it('parses an inverse section', function() { - equals(ast_for("{{#foo}} bar {{^}} baz {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"); + equals(ast_for("{{#foo}} bar {{^}} baz {{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"); }); it('parses an inverse (else-style) section', function() { - equals(ast_for("{{#foo}} bar {{else}} baz {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"); + equals(ast_for("{{#foo}} bar {{else}} baz {{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n CONTENT[ ' baz ' ]\n"); }); it('parses empty blocks', function() { - equals(ast_for("{{#foo}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n"); + equals(ast_for("{{#foo}}{{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n"); }); it('parses empty blocks with empty inverse section', function() { - equals(ast_for("{{#foo}}{{^}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n"); + equals(ast_for("{{#foo}}{{^}}{{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n {{^}}\n"); }); it('parses empty blocks with empty inverse (else-style) section', function() { - equals(ast_for("{{#foo}}{{else}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n"); + equals(ast_for("{{#foo}}{{else}}{{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n {{^}}\n"); }); it('parses non-empty blocks with empty inverse section', function() { - equals(ast_for("{{#foo}} bar {{^}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"); + equals(ast_for("{{#foo}} bar {{^}}{{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"); }); it('parses non-empty blocks with empty inverse (else-style) section', function() { - equals(ast_for("{{#foo}} bar {{else}}{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"); + equals(ast_for("{{#foo}} bar {{else}}{{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n CONTENT[ ' bar ' ]\n {{^}}\n"); }); it('parses empty blocks with non-empty inverse section', function() { - equals(ast_for("{{#foo}}{{^}} bar {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"); + equals(ast_for("{{#foo}}{{^}} bar {{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"); }); it('parses empty blocks with non-empty inverse (else-style) section', function() { - equals(ast_for("{{#foo}}{{else}} bar {{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"); + equals(ast_for("{{#foo}}{{else}} bar {{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n {{^}}\n CONTENT[ ' bar ' ]\n"); }); it('parses a standalone inverse section', function() { - equals(ast_for("{{^foo}}bar{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n"); + equals(ast_for("{{^foo}}bar{{/foo}}"), "BLOCK:\n ID:foo []\n {{^}}\n CONTENT[ 'bar' ]\n"); }); it('parses a standalone inverse section', function() { - equals(ast_for("{{else foo}}bar{{/foo}}"), "BLOCK:\n {{ ID:foo [] }}\n {{^}}\n CONTENT[ 'bar' ]\n"); + equals(ast_for("{{else foo}}bar{{/foo}}"), "BLOCK:\n ID:foo []\n {{^}}\n CONTENT[ 'bar' ]\n"); }); it("raises if there's a Parse error", function() { diff --git a/src/handlebars.yy b/src/handlebars.yy index ccc0d2248..233088ba3 100644 --- a/src/handlebars.yy +++ b/src/handlebars.yy @@ -17,16 +17,20 @@ statement | block -> $1 | rawBlock -> $1 | partial -> $1 - | CONTENT -> new yy.ContentNode($1, @$) + | content -> $1 | COMMENT -> new yy.CommentNode(yy.stripComment($1), yy.stripFlags($1, $1), @$) ; +content + : CONTENT -> new yy.ContentNode($1, @$) + ; + rawBlock - : openRawBlock CONTENT END_RAW_BLOCK -> new yy.RawBlockNode($1, $2, $3, @$) + : openRawBlock content END_RAW_BLOCK -> yy.prepareRawBlock($1, $2, $3, @$) ; openRawBlock - : OPEN_RAW_BLOCK sexpr CLOSE_RAW_BLOCK -> new yy.MustacheNode($2, null, '', '', @$) + : OPEN_RAW_BLOCK sexpr CLOSE_RAW_BLOCK -> { sexpr: $2 } ; block @@ -35,11 +39,11 @@ block ; openBlock - : OPEN_BLOCK sexpr CLOSE -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), @$) + : OPEN_BLOCK sexpr CLOSE -> { sexpr: $2, strip: yy.stripFlags($1, $3) } ; openInverse - : OPEN_INVERSE sexpr CLOSE -> new yy.MustacheNode($2, null, $1, yy.stripFlags($1, $3), @$) + : OPEN_INVERSE sexpr CLOSE -> { sexpr: $2, strip: yy.stripFlags($1, $3) } ; inverseAndProgram From a5d0491d57f53425ef155a3a803a881371bf07b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mu=C3=B1oz?= Date: Wed, 5 Nov 2014 11:00:39 -0500 Subject: [PATCH 2/2] Remove RawBlockNode in favor of prepareRawBlock helper --- lib/handlebars/compiler/ast.js | 8 -------- lib/handlebars/compiler/helpers.js | 4 +++- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index 05de762b6..35a60dbed 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -93,14 +93,6 @@ var AST = { } }, - RawBlockNode: function(sexpr, content, locInfo) { - LocationInfo.call(this, locInfo); - - this.type = 'block'; - this.sexpr = sexpr; - this.program = new AST.ProgramNode([content], {}, locInfo); - }, - ContentNode: function(string, locInfo) { LocationInfo.call(this, locInfo); this.type = "content"; diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js index c9a35c24d..02b307b05 100644 --- a/lib/handlebars/compiler/helpers.js +++ b/lib/handlebars/compiler/helpers.js @@ -23,7 +23,9 @@ export function prepareRawBlock(openRawBlock, content, close, locInfo) { throw new Exception(openRawBlock.sexpr.id.original + " doesn't match " + close, errorNode); } - return new this.RawBlockNode(openRawBlock.sexpr, content, locInfo); + var program = new this.ProgramNode([content], {}, locInfo); + + return new this.BlockNode(openRawBlock.sexpr, program, undefined, undefined, locInfo); } export function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) {