Merge pull request #906 from mmun/block-params
Add parser support for block params
This commit is contained in:
@@ -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;
|
||||
},
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
+2
-2
@@ -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() {
|
||||
|
||||
+9
-1
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
});
|
||||
|
||||
+3
-1
@@ -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';
|
||||
|
||||
|
||||
+8
-5
@@ -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}]
|
||||
;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user