From 2459c292a4d6fb0552eb9be3cecd955093ed853b Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Tue, 10 Sep 2019 19:21:52 -0600 Subject: caddyfile: Improve Dispenser.NextBlock() to support nesting --- caddyconfig/caddyfile/dispenser.go | 75 +++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 21 deletions(-) (limited to 'caddyconfig/caddyfile/dispenser.go') diff --git a/caddyconfig/caddyfile/dispenser.go b/caddyconfig/caddyfile/dispenser.go index 938c985..de67ee1 100755 --- a/caddyconfig/caddyfile/dispenser.go +++ b/caddyconfig/caddyfile/dispenser.go @@ -123,18 +123,39 @@ func (d *Dispenser) NextLine() bool { // NextBlock can be used as the condition of a for loop // to load the next token as long as it opens a block or -// is already in a block. It returns true if a token was -// loaded, or false when the block's closing curly brace -// was loaded and thus the block ended. Nested blocks are -// not supported. -func (d *Dispenser) NextBlock() bool { - if d.nesting > 0 { - d.Next() +// is already in a block nested more than initialNestingLevel. +// In other words, a loop over NextBlock() will iterate +// all tokens in the block assuming the next token is an +// open curly brace, until the matching closing brace. +// The open and closing brace tokens for the outer-most +// block will be consumed internally and omitted from +// the iteration. +// +// Proper use of this method looks like this: +// +// for nesting := d.Nesting(); d.NextBlock(nesting); { +// } +// +// However, in simple cases where it is known that the +// Dispenser is new and has not already traversed state +// by a loop over NextBlock(), this will do: +// +// for d.NextBlock(0) { +// } +// +// As with other token parsing logic, a loop over +// NextBlock() should be contained within a loop over +// Next(), as it is usually prudent to skip the initial +// token. +func (d *Dispenser) NextBlock(initialNestingLevel int) bool { + if d.nesting > initialNestingLevel { + if !d.Next() { + return false // should be EOF error + } if d.Val() == "}" { d.nesting-- - return false } - return true + return d.nesting > initialNestingLevel } if !d.nextOnSameLine() { // block must open on same line return false @@ -143,19 +164,18 @@ func (d *Dispenser) NextBlock() bool { d.cursor-- // roll back if not opening brace return false } - d.Next() + d.Next() // consume open curly brace if d.Val() == "}" { - // open and then closed right away - return false + return false // open and then closed right away } d.nesting++ return true } -// Nested returns true if the token is currently nested -// inside a block (i.e. an open curly brace was consumed). -func (d *Dispenser) Nested() bool { - return d.nesting > 0 +// Nesting returns the current nesting level. Necessary +// if using NextBlock() +func (d *Dispenser) Nesting() int { + return d.nesting } // Val gets the text of the current token. If there is no token @@ -230,19 +250,32 @@ func (d *Dispenser) RemainingArgs() []string { // NewFromNextTokens returns a new dispenser with a copy of // the tokens from the current token until the end of the // "directive" whether that be to the end of the line or -// the end of a block that starts at the end of the line. +// the end of a block that starts at the end of the line; +// in other words, until the end of the segment. func (d *Dispenser) NewFromNextTokens() *Dispenser { tkns := []Token{d.Token()} for d.NextArg() { tkns = append(tkns, d.Token()) } - for d.NextBlock() { - for d.Nested() { + var openedBlock bool + for nesting := d.Nesting(); d.NextBlock(nesting); { + if !openedBlock { + // because NextBlock() consumes the initial open + // curly brace, we rewind here to append it, since + // our case is special in that we want to include + // all the tokens including surrounding curly braces + // for a new dispenser to have + d.Prev() tkns = append(tkns, d.Token()) - d.NextBlock() + d.Next() + openedBlock = true } + tkns = append(tkns, d.Token()) + } + if openedBlock { + // include closing brace accordingly + tkns = append(tkns, d.Token()) } - tkns = append(tkns, d.Token()) return NewDispenser(tkns) } -- cgit v1.2.3