summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Muñoz <im.mmun@gmail.com>2014-11-11 21:35:10 -0500
committerMartin Muñoz <im.mmun@gmail.com>2014-11-11 21:35:10 -0500
commitb8a9f7264d3b6ac48514272bf35291736cedad00 (patch)
tree08fdbc4c7e73e63f98a136285cc7a9958bec719f
parentb3b5b358895566c04b3a9776ac81c5bcf245e27c (diff)
downloadhandlebars.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.js3
-rw-r--r--lib/handlebars/compiler/helpers.js4
-rw-r--r--lib/handlebars/compiler/printer.js9
-rw-r--r--spec/ast.js2
-rw-r--r--spec/compiler.js4
-rw-r--r--spec/parser.js10
-rw-r--r--spec/tokenizer.js14
-rw-r--r--src/handlebars.l4
-rw-r--r--src/handlebars.yy13
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}]
;
-