diff options
author | Martin Muñoz <im.mmun@gmail.com> | 2014-11-11 21:35:10 -0500 |
---|---|---|
committer | Martin Muñoz <im.mmun@gmail.com> | 2014-11-11 21:35:10 -0500 |
commit | b8a9f7264d3b6ac48514272bf35291736cedad00 (patch) | |
tree | 08fdbc4c7e73e63f98a136285cc7a9958bec719f | |
parent | b3b5b358895566c04b3a9776ac81c5bcf245e27c (diff) | |
download | handlebars.js-b8a9f7264d3b6ac48514272bf35291736cedad00.zip handlebars.js-b8a9f7264d3b6ac48514272bf35291736cedad00.tar.gz handlebars.js-b8a9f7264d3b6ac48514272bf35291736cedad00.tar.bz2 |
Add parser support for block params
-rw-r--r-- | lib/handlebars/compiler/ast.js | 3 | ||||
-rw-r--r-- | lib/handlebars/compiler/helpers.js | 4 | ||||
-rw-r--r-- | lib/handlebars/compiler/printer.js | 9 | ||||
-rw-r--r-- | spec/ast.js | 2 | ||||
-rw-r--r-- | spec/compiler.js | 4 | ||||
-rw-r--r-- | spec/parser.js | 10 | ||||
-rw-r--r-- | spec/tokenizer.js | 14 | ||||
-rw-r--r-- | src/handlebars.l | 4 | ||||
-rw-r--r-- | src/handlebars.yy | 13 |
9 files changed, 51 insertions, 12 deletions
diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js index 35a60db..0bc70e9 100644 --- a/lib/handlebars/compiler/ast.js +++ b/lib/handlebars/compiler/ast.js @@ -9,10 +9,11 @@ function LocationInfo(locInfo) { } var AST = { - ProgramNode: function(statements, strip, locInfo) { + ProgramNode: function(statements, blockParams, strip, locInfo) { LocationInfo.call(this, locInfo); this.type = "program"; this.statements = statements; + this.blockParams = blockParams; this.strip = strip; }, diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js index d9b7b14..50a3c53 100644 --- a/lib/handlebars/compiler/helpers.js +++ b/lib/handlebars/compiler/helpers.js @@ -23,7 +23,7 @@ export function prepareRawBlock(openRawBlock, content, close, locInfo) { throw new Exception(openRawBlock.sexpr.id.original + " doesn't match " + close, errorNode); } - var program = new this.ProgramNode([content], {}, locInfo); + var program = new this.ProgramNode([content], null, {}, locInfo); return new this.BlockNode(openRawBlock.sexpr, program, undefined, undefined, locInfo); } @@ -40,6 +40,8 @@ export function prepareBlock(openBlock, program, inverseAndProgram, close, inver throw new Exception(openBlock.sexpr.id.original + ' doesn\'t match ' + close.path.original, errorNode); } + program.blockParams = openBlock.blockParams; + // Safely handle a chained inverse that does not have a non-conditional inverse // (i.e. both inverseAndProgram AND close are undefined) if (!close) { diff --git a/lib/handlebars/compiler/printer.js b/lib/handlebars/compiler/printer.js index c329373..e93652c 100644 --- a/lib/handlebars/compiler/printer.js +++ b/lib/handlebars/compiler/printer.js @@ -26,6 +26,15 @@ PrintVisitor.prototype.program = function(program) { statements = program.statements, i, l; + if (program.blockParams) { + var blockParams = "BLOCK PARAMS: ["; + for(i=0, l=program.blockParams.length; i<l; i++) { + blockParams += " " + program.blockParams[i]; + } + blockParams += " ]"; + out += this.pad(blockParams); + } + for(i=0, l=statements.length; i<l; i++) { out = out + this.accept(statements[i]); } diff --git a/spec/ast.js b/spec/ast.js index 9b3edb0..ef2ef68 100644 --- a/spec/ast.js +++ b/spec/ast.js @@ -192,7 +192,7 @@ describe('ast', function() { describe('ProgramNode', function(){ it('storing location info', function(){ - var pn = new handlebarsEnv.AST.ProgramNode([], {}, LOCATION_INFO); + var pn = new handlebarsEnv.AST.ProgramNode([], null, {}, LOCATION_INFO); testLocationInfoStorage(pn); }); }); diff --git a/spec/compiler.js b/spec/compiler.js index 250dbc7..eead00b 100644 --- a/spec/compiler.js +++ b/spec/compiler.js @@ -41,7 +41,7 @@ describe('compiler', function() { }); it('can utilize AST instance', function() { - equal(Handlebars.compile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")], {}))(), 'Hello'); + equal(Handlebars.compile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")], null, {}))(), 'Hello'); }); it("can pass through an empty string", function() { @@ -60,7 +60,7 @@ describe('compiler', function() { }); it('can utilize AST instance', function() { - equal(/return "Hello"/.test(Handlebars.precompile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]), {})), true); + equal(/return "Hello"/.test(Handlebars.precompile(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")]), null, {})), true); }); it("can pass through an empty string", function() { diff --git a/spec/parser.js b/spec/parser.js index f587365..0569229 100644 --- a/spec/parser.js +++ b/spec/parser.js @@ -157,6 +157,14 @@ describe('parser', function() { }, Error); }); + it('parses block with block params', function() { + equals(ast_for("{{#foo as |bar baz|}}content{{/foo}}"), "BLOCK:\n ID:foo []\n PROGRAM:\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"); + }); + + it('parses inverse block with block params', function() { + equals(ast_for("{{^foo as |bar baz|}}content{{/foo}}"), "BLOCK:\n ID:foo []\n {{^}}\n BLOCK PARAMS: [ bar baz ]\n CONTENT[ 'content' ]\n"); + }); + it("raises if there's a Parse error", function() { shouldThrow(function() { ast_for("foo{{^}}bar"); @@ -193,7 +201,7 @@ describe('parser', function() { describe('externally compiled AST', function() { it('can pass through an already-compiled AST', function() { - equals(ast_for(new Handlebars.AST.ProgramNode([ new Handlebars.AST.ContentNode("Hello")])), "CONTENT[ \'Hello\' ]\n"); + equals(ast_for(new Handlebars.AST.ProgramNode([new Handlebars.AST.ContentNode("Hello")], null)), "CONTENT[ \'Hello\' ]\n"); }); }); }); diff --git a/spec/tokenizer.js b/spec/tokenizer.js index 6f5cc38..93ed97c 100644 --- a/spec/tokenizer.js +++ b/spec/tokenizer.js @@ -399,4 +399,18 @@ describe('Tokenizer', function() { var result = tokenize("{{foo (bar (lol true) false) (baz 1) (blah 'b') (blorg \"c\")}}"); shouldMatchTokens(result, ['OPEN', 'ID', 'OPEN_SEXPR', 'ID', 'OPEN_SEXPR', 'ID', 'BOOLEAN', 'CLOSE_SEXPR', 'BOOLEAN', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'NUMBER', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'STRING', 'CLOSE_SEXPR', 'OPEN_SEXPR', 'ID', 'STRING', 'CLOSE_SEXPR', 'CLOSE']); }); + + it('tokenizes block params', function() { + var result = tokenize("{{#foo as |bar|}}"); + shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + + var result = tokenize("{{#foo as |bar baz|}}"); + shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + + var result = tokenize("{{#foo as | bar baz |}}"); + shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + + var result = tokenize("{{#foo as as | bar baz |}}"); + shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'ID', 'OPEN_BLOCK_PARAMS', 'ID', 'ID', 'CLOSE_BLOCK_PARAMS', 'CLOSE']); + }); }); diff --git a/src/handlebars.l b/src/handlebars.l index f3c925c..ace0263 100644 --- a/src/handlebars.l +++ b/src/handlebars.l @@ -12,7 +12,7 @@ function strip(start, end) { LEFT_STRIP "~" RIGHT_STRIP "~" -LOOKAHEAD [=~}\s\/.)] +LOOKAHEAD [=~}\s\/.)|] LITERAL_LOOKAHEAD [~}\s)] /* @@ -103,6 +103,8 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD} <mu>"true"/{LITERAL_LOOKAHEAD} return 'BOOLEAN'; <mu>"false"/{LITERAL_LOOKAHEAD} return 'BOOLEAN'; <mu>\-?[0-9]+(?:\.[0-9]+)?/{LITERAL_LOOKAHEAD} return 'NUMBER'; +<mu>"as"\s+"|" return 'OPEN_BLOCK_PARAMS'; +<mu>"|" return 'CLOSE_BLOCK_PARAMS'; <mu>{ID} return 'ID'; diff --git a/src/handlebars.yy b/src/handlebars.yy index 5448966..775d5ca 100644 --- a/src/handlebars.yy +++ b/src/handlebars.yy @@ -9,7 +9,7 @@ root ; program - : statement* -> new yy.ProgramNode(yy.prepareProgram($1), {}, @$) + : statement* -> new yy.ProgramNode(yy.prepareProgram($1), null, {}, @$) ; statement @@ -39,11 +39,11 @@ block ; openBlock - : OPEN_BLOCK sexpr CLOSE -> { sexpr: $2, strip: yy.stripFlags($1, $3) } + : OPEN_BLOCK sexpr blockParams? CLOSE -> { sexpr: $2, blockParams: $3, strip: yy.stripFlags($1, $4) } ; openInverse - : OPEN_INVERSE sexpr CLOSE -> { sexpr: $2, strip: yy.stripFlags($1, $3) } + : OPEN_INVERSE sexpr blockParams? CLOSE -> { sexpr: $2, blockParams: $3, strip: yy.stripFlags($1, $4) } ; openInverseChain @@ -57,7 +57,7 @@ inverseAndProgram inverseChain : openInverseChain program inverseChain? { var inverse = yy.prepareBlock($1, $2, $3, $3, false, @$), - program = new yy.ProgramNode(yy.prepareProgram([inverse]), {}, @$); + program = new yy.ProgramNode(yy.prepareProgram([inverse]), null, {}, @$); program.inverse = inverse; @@ -104,6 +104,10 @@ hashSegment : ID EQUALS param -> [$1, $3] ; +blockParams + : OPEN_BLOCK_PARAMS ID+ CLOSE_BLOCK_PARAMS -> $2 + ; + partialName : path -> new yy.PartialNameNode($1, @$) | STRING -> new yy.PartialNameNode(new yy.StringNode($1, @$), @$) @@ -122,4 +126,3 @@ pathSegments : pathSegments SEP ID { $1.push({part: $3, separator: $2}); $$ = $1; } | ID -> [{part: $1}] ; - |