summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkpdecker <kpdecker@gmail.com>2015-08-19 06:59:32 -0700
committerkpdecker <kpdecker@gmail.com>2015-08-22 10:59:34 -0700
commit408192ba9f262bb82be88091ab3ec3c16dc02c6d (patch)
tree5fb9fa403eba8b1e078096b14aaeebf5e8026292
parent2a4a5447f560723a2c898e0a4d97cd929131bba6 (diff)
downloadhandlebars.js-408192ba9f262bb82be88091ab3ec3c16dc02c6d.zip
handlebars.js-408192ba9f262bb82be88091ab3ec3c16dc02c6d.tar.gz
handlebars.js-408192ba9f262bb82be88091ab3ec3c16dc02c6d.tar.bz2
Add decorator parsing
-rw-r--r--docs/compiler-api.md27
-rw-r--r--lib/handlebars/compiler/helpers.js11
-rw-r--r--lib/handlebars/compiler/printer.js8
-rw-r--r--lib/handlebars/compiler/visitor.js2
-rw-r--r--lib/handlebars/compiler/whitespace-control.js2
-rw-r--r--spec/parser.js14
-rw-r--r--spec/tokenizer.js9
-rw-r--r--spec/visitor.js2
-rw-r--r--src/handlebars.l4
-rw-r--r--src/handlebars.yy2
10 files changed, 74 insertions, 7 deletions
diff --git a/docs/compiler-api.md b/docs/compiler-api.md
index 81438d2..5722dcc 100644
--- a/docs/compiler-api.md
+++ b/docs/compiler-api.md
@@ -120,6 +120,33 @@ interface CommentStatement <: Statement {
}
```
+
+```java
+interface Decorator <: Statement {
+ type: "Decorator";
+
+ path: PathExpression | Literal;
+ params: [ Expression ];
+ hash: Hash;
+
+ strip: StripFlags | null;
+}
+
+interface DecoratorBlock <: Statement {
+ type: "DecoratorBlock";
+ path: PathExpression | Literal;
+ params: [ Expression ];
+ hash: Hash;
+
+ program: Program | null;
+
+ openStrip: StripFlags | null;
+ closeStrip: StripFlags | null;
+}
+```
+
+Decorator paths only utilize the `path.original` value and as a consequence do not support depthed evaluation.
+
### Expressions
```java
diff --git a/lib/handlebars/compiler/helpers.js b/lib/handlebars/compiler/helpers.js
index 9c40f0d..e09a08d 100644
--- a/lib/handlebars/compiler/helpers.js
+++ b/lib/handlebars/compiler/helpers.js
@@ -84,8 +84,9 @@ export function prepareMustache(path, params, hash, open, strip, locInfo) {
let escapeFlag = open.charAt(3) || open.charAt(2),
escaped = escapeFlag !== '{' && escapeFlag !== '&';
+ let decorator = (/\*/.test(open));
return {
- type: 'MustacheStatement',
+ type: decorator ? 'Decorator' : 'MustacheStatement',
path,
params,
hash,
@@ -124,12 +125,18 @@ export function prepareBlock(openBlock, program, inverseAndProgram, close, inver
validateClose(openBlock, close);
}
+ let decorator = (/\*/.test(openBlock.open));
+
program.blockParams = openBlock.blockParams;
let inverse,
inverseStrip;
if (inverseAndProgram) {
+ if (decorator) {
+ throw new Exception('Unexpected inverse block on decorator', inverseAndProgram);
+ }
+
if (inverseAndProgram.chain) {
inverseAndProgram.program.body[0].closeStrip = close.strip;
}
@@ -145,7 +152,7 @@ export function prepareBlock(openBlock, program, inverseAndProgram, close, inver
}
return {
- type: 'BlockStatement',
+ type: decorator ? 'DecoratorBlock' : 'BlockStatement',
path: openBlock.path,
params: openBlock.params,
hash: openBlock.hash,
diff --git a/lib/handlebars/compiler/printer.js b/lib/handlebars/compiler/printer.js
index cf7aa48..66e7c7d 100644
--- a/lib/handlebars/compiler/printer.js
+++ b/lib/handlebars/compiler/printer.js
@@ -48,11 +48,15 @@ PrintVisitor.prototype.Program = function(program) {
PrintVisitor.prototype.MustacheStatement = function(mustache) {
return this.pad('{{ ' + this.SubExpression(mustache) + ' }}');
};
+PrintVisitor.prototype.Decorator = function(mustache) {
+ return this.pad('{{ DIRECTIVE ' + this.SubExpression(mustache) + ' }}');
+};
-PrintVisitor.prototype.BlockStatement = function(block) {
+PrintVisitor.prototype.BlockStatement =
+PrintVisitor.prototype.DecoratorBlock = function(block) {
let out = '';
- out += this.pad('BLOCK:');
+ out += this.pad((block.type === 'DecoratorBlock' ? 'DIRECTIVE ' : '') + 'BLOCK:');
this.padding++;
out += this.pad(this.SubExpression(block));
if (block.program) {
diff --git a/lib/handlebars/compiler/visitor.js b/lib/handlebars/compiler/visitor.js
index cc54c53..2c504d1 100644
--- a/lib/handlebars/compiler/visitor.js
+++ b/lib/handlebars/compiler/visitor.js
@@ -76,8 +76,10 @@ Visitor.prototype = {
},
MustacheStatement: visitSubExpression,
+ Decorator: visitSubExpression,
BlockStatement: visitBlock,
+ DecoratorBlock: visitBlock,
PartialStatement: visitPartial,
PartialBlockStatement: function(partial) {
diff --git a/lib/handlebars/compiler/whitespace-control.js b/lib/handlebars/compiler/whitespace-control.js
index 6c8a986..e11483c 100644
--- a/lib/handlebars/compiler/whitespace-control.js
+++ b/lib/handlebars/compiler/whitespace-control.js
@@ -63,6 +63,7 @@ WhitespaceControl.prototype.Program = function(program) {
};
WhitespaceControl.prototype.BlockStatement =
+WhitespaceControl.prototype.DecoratorBlock =
WhitespaceControl.prototype.PartialBlockStatement = function(block) {
this.accept(block.program);
this.accept(block.inverse);
@@ -124,6 +125,7 @@ WhitespaceControl.prototype.PartialBlockStatement = function(block) {
return strip;
};
+WhitespaceControl.prototype.Decorator =
WhitespaceControl.prototype.MustacheStatement = function(mustache) {
return mustache.strip;
};
diff --git a/spec/parser.js b/spec/parser.js
index 5b60f93..3b7e3e4 100644
--- a/spec/parser.js
+++ b/spec/parser.js
@@ -247,6 +247,20 @@ describe('parser', function() {
});
});
+ describe('directives', function() {
+ it('should parse block directives', function() {
+ equals(astFor('{{#* foo}}{{/foo}}'), 'DIRECTIVE BLOCK:\n PATH:foo []\n PROGRAM:\n');
+ });
+ it('should parse directives', function() {
+ equals(astFor('{{* foo}}'), '{{ DIRECTIVE PATH:foo [] }}\n');
+ });
+ it('should fail if directives have inverse', function() {
+ shouldThrow(function() {
+ astFor('{{#* foo}}{{^}}{{/foo}}');
+ }, Error, /Unexpected inverse/);
+ });
+ });
+
it('GH1024 - should track program location properly', function() {
var p = Handlebars.parse('\n'
+ ' {{#if foo}}\n'
diff --git a/spec/tokenizer.js b/spec/tokenizer.js
index f170704..dc077ce 100644
--- a/spec/tokenizer.js
+++ b/spec/tokenizer.js
@@ -241,6 +241,15 @@ describe('Tokenizer', function() {
shouldMatchTokens(result, ['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']);
});
+ it('tokenizes directives', function() {
+ shouldMatchTokens(
+ tokenize('{{#*foo}}content{{/foo}}'),
+ ['OPEN_BLOCK', 'ID', 'CLOSE', 'CONTENT', 'OPEN_ENDBLOCK', 'ID', 'CLOSE']);
+ shouldMatchTokens(
+ tokenize('{{*foo}}'),
+ ['OPEN', 'ID', 'CLOSE']);
+ });
+
it('tokenizes inverse sections as "INVERSE"', function() {
shouldMatchTokens(tokenize('{{^}}'), ['INVERSE']);
shouldMatchTokens(tokenize('{{else}}'), ['INVERSE']);
diff --git a/spec/visitor.js b/spec/visitor.js
index 3e2d523..d3fb795 100644
--- a/spec/visitor.js
+++ b/spec/visitor.js
@@ -9,6 +9,8 @@ describe('Visitor', function() {
var visitor = new Handlebars.Visitor();
visitor.accept(Handlebars.parse('{{foo}}{{#foo (bar 1 "1" true undefined null) foo=@data}}{{!comment}}{{> bar }} {{/foo}}'));
visitor.accept(Handlebars.parse('{{#> bar }} {{/bar}}'));
+ visitor.accept(Handlebars.parse('{{#* bar }} {{/bar}}'));
+ visitor.accept(Handlebars.parse('{{* bar }}'));
});
it('should traverse to stubs', function() {
diff --git a/src/handlebars.l b/src/handlebars.l
index 39a7884..4c3c304 100644
--- a/src/handlebars.l
+++ b/src/handlebars.l
@@ -81,7 +81,7 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
}
<mu>"{{"{LEFT_STRIP}?">" return 'OPEN_PARTIAL';
<mu>"{{"{LEFT_STRIP}?"#>" return 'OPEN_PARTIAL_BLOCK';
-<mu>"{{"{LEFT_STRIP}?"#" return 'OPEN_BLOCK';
+<mu>"{{"{LEFT_STRIP}?"#""*"? return 'OPEN_BLOCK';
<mu>"{{"{LEFT_STRIP}?"/" return 'OPEN_ENDBLOCK';
<mu>"{{"{LEFT_STRIP}?"^"\s*{RIGHT_STRIP}?"}}" this.popState(); return 'INVERSE';
<mu>"{{"{LEFT_STRIP}?\s*"else"\s*{RIGHT_STRIP}?"}}" this.popState(); return 'INVERSE';
@@ -98,7 +98,7 @@ ID [^\s!"#%-,\.\/;->@\[-\^`\{-~]+/{LOOKAHEAD}
this.popState();
return 'COMMENT';
}
-<mu>"{{"{LEFT_STRIP}? return 'OPEN';
+<mu>"{{"{LEFT_STRIP}?"*"? return 'OPEN';
<mu>"=" return 'EQUALS';
<mu>".." return 'ID';
diff --git a/src/handlebars.yy b/src/handlebars.yy
index e94ab51..ce06498 100644
--- a/src/handlebars.yy
+++ b/src/handlebars.yy
@@ -52,7 +52,7 @@ block
;
openBlock
- : OPEN_BLOCK helperName param* hash? blockParams? CLOSE -> { path: $2, params: $3, hash: $4, blockParams: $5, strip: yy.stripFlags($1, $6) }
+ : OPEN_BLOCK helperName param* hash? blockParams? CLOSE -> { open: $1, path: $2, params: $3, hash: $4, blockParams: $5, strip: yy.stripFlags($1, $6) }
;
openInverse