diff options
author | kpdecker <kpdecker@gmail.com> | 2015-08-19 06:59:32 -0700 |
---|---|---|
committer | kpdecker <kpdecker@gmail.com> | 2015-08-22 10:59:34 -0700 |
commit | 408192ba9f262bb82be88091ab3ec3c16dc02c6d (patch) | |
tree | 5fb9fa403eba8b1e078096b14aaeebf5e8026292 | |
parent | 2a4a5447f560723a2c898e0a4d97cd929131bba6 (diff) | |
download | handlebars.js-408192ba9f262bb82be88091ab3ec3c16dc02c6d.zip handlebars.js-408192ba9f262bb82be88091ab3ec3c16dc02c6d.tar.gz handlebars.js-408192ba9f262bb82be88091ab3ec3c16dc02c6d.tar.bz2 |
Add decorator parsing
-rw-r--r-- | docs/compiler-api.md | 27 | ||||
-rw-r--r-- | lib/handlebars/compiler/helpers.js | 11 | ||||
-rw-r--r-- | lib/handlebars/compiler/printer.js | 8 | ||||
-rw-r--r-- | lib/handlebars/compiler/visitor.js | 2 | ||||
-rw-r--r-- | lib/handlebars/compiler/whitespace-control.js | 2 | ||||
-rw-r--r-- | spec/parser.js | 14 | ||||
-rw-r--r-- | spec/tokenizer.js | 9 | ||||
-rw-r--r-- | spec/visitor.js | 2 | ||||
-rw-r--r-- | src/handlebars.l | 4 | ||||
-rw-r--r-- | src/handlebars.yy | 2 |
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 |