summaryrefslogtreecommitdiff
path: root/caddyconfig/caddyfile/parse.go
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-08-21 10:46:35 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2019-08-21 10:46:35 -0600
commitc9980fd3671d873a7197a5ac4d6ac9d6b046abb6 (patch)
tree75c301ab10590fb5f7d5b869a3424b8d46176bbf /caddyconfig/caddyfile/parse.go
parentc4159ef76d279d6a84257b24dbe97430af32eb1e (diff)
Refactor Caddyfile adapter and module registration
Use piles from which to draw config values. Module values can return their name, so now we can do two-way mapping from value to name and name to value; whereas before we could only map name to value. This was problematic with the Caddyfile adapter since it receives values and needs to know the name to put in the config.
Diffstat (limited to 'caddyconfig/caddyfile/parse.go')
-rwxr-xr-xcaddyconfig/caddyfile/parse.go112
1 files changed, 71 insertions, 41 deletions
diff --git a/caddyconfig/caddyfile/parse.go b/caddyconfig/caddyfile/parse.go
index cc91e3d..e5b25fc 100755
--- a/caddyconfig/caddyfile/parse.go
+++ b/caddyconfig/caddyfile/parse.go
@@ -28,12 +28,12 @@ import (
// Directives that do not appear in validDirectives will cause
// an error. If you do not want to check for valid directives,
// pass in nil instead.
-func Parse(filename string, input io.Reader, validDirectives []string) ([]ServerBlock, error) {
+func Parse(filename string, input io.Reader) ([]ServerBlock, error) {
tokens, err := allTokens(input)
if err != nil {
return nil, err
}
- p := parser{Dispenser: NewDispenser(filename, tokens), validDirectives: validDirectives}
+ p := parser{Dispenser: NewDispenser(filename, tokens)}
return p.parseAll()
}
@@ -56,9 +56,9 @@ func allTokens(input io.Reader) ([]Token, error) {
type parser struct {
*Dispenser
block ServerBlock // current server block being parsed
- validDirectives []string // a directive must be valid or it's an error
eof bool // if we encounter a valid EOF in a hard place
definedSnippets map[string][]Token
+ nesting int
}
func (p *parser) parseAll() ([]ServerBlock, error) {
@@ -72,14 +72,16 @@ func (p *parser) parseAll() ([]ServerBlock, error) {
if len(p.block.Keys) > 0 {
blocks = append(blocks, p.block)
}
+ if p.nesting > 0 {
+ return blocks, p.EOFErr()
+ }
}
return blocks, nil
}
func (p *parser) parseOne() error {
- p.block = ServerBlock{Tokens: make(map[string][]Token)}
-
+ p.block = ServerBlock{}
return p.begin()
}
@@ -186,7 +188,7 @@ func (p *parser) blockContents() error {
return err
}
- // Only look for close curly brace if there was an opening
+ // only look for close curly brace if there was an opening
if errOpenCurlyBrace == nil {
err = p.closeCurlyBrace()
if err != nil {
@@ -205,6 +207,7 @@ func (p *parser) directives() error {
for p.Next() {
// end of server block
if p.Val() == "}" {
+ // p.nesting has already been decremented
break
}
@@ -218,11 +221,15 @@ func (p *parser) directives() error {
continue
}
- // normal case: parse a directive on this line
+ // normal case: parse a directive as a new segment
+ // (a "segment" is a line which starts with a directive
+ // and which ends at the end of the line or at the end of
+ // the block that is opened at the end of the line)
if err := p.directive(); err != nil {
return err
}
}
+
return nil
}
@@ -345,25 +352,24 @@ func (p *parser) doSingleImport(importFile string) ([]Token, error) {
// are loaded into the current server block for later use
// by directive setup functions.
func (p *parser) directive() error {
- dir := replaceEnvVars(p.Val())
- nesting := 0
+ // evaluate any env vars in directive token
+ p.tokens[p.cursor].Text = replaceEnvVars(p.tokens[p.cursor].Text)
- if !p.validDirective(dir) {
- return p.Errf("Unknown directive '%s'", dir)
- }
+ // a segment is a list of tokens associated with this directive
+ var segment Segment
- // The directive itself is appended as a relevant token
- p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
+ // the directive itself is appended as a relevant token
+ segment = append(segment, p.Token())
for p.Next() {
if p.Val() == "{" {
- nesting++
- } else if p.isNewLine() && nesting == 0 {
+ p.nesting++
+ } else if p.isNewLine() && p.nesting == 0 {
p.cursor-- // read too far
break
- } else if p.Val() == "}" && nesting > 0 {
- nesting--
- } else if p.Val() == "}" && nesting == 0 {
+ } else if p.Val() == "}" && p.nesting > 0 {
+ p.nesting--
+ } else if p.Val() == "}" && p.nesting == 0 {
return p.Err("Unexpected '}' because no matching opening brace")
} else if p.Val() == "import" && p.isNewLine() {
if err := p.doImport(); err != nil {
@@ -373,12 +379,15 @@ func (p *parser) directive() error {
continue
}
p.tokens[p.cursor].Text = replaceEnvVars(p.tokens[p.cursor].Text)
- p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
+ segment = append(segment, p.Token())
}
- if nesting > 0 {
+ p.block.Segments = append(p.block.Segments, segment)
+
+ if p.nesting > 0 {
return p.EOFErr()
}
+
return nil
}
@@ -404,19 +413,6 @@ func (p *parser) closeCurlyBrace() error {
return nil
}
-// validDirective returns true if dir is in p.validDirectives.
-func (p *parser) validDirective(dir string) bool {
- if p.validDirectives == nil {
- return true
- }
- for _, d := range p.validDirectives {
- if d == dir {
- return true
- }
- }
- return false
-}
-
// replaceEnvVars replaces environment variables that appear in the token
// and understands both the $UNIX and %WINDOWS% syntaxes.
func replaceEnvVars(s string) string {
@@ -447,13 +443,6 @@ func replaceEnvReferences(s, refStart, refEnd string) string {
return s
}
-// ServerBlock associates any number of keys (usually addresses
-// of some sort) with tokens (grouped by directive name).
-type ServerBlock struct {
- Keys []string
- Tokens map[string][]Token
-}
-
func (p *parser) isSnippet() (bool, string) {
keys := p.block.Keys
// A snippet block is a single key with parens. Nothing else qualifies.
@@ -480,6 +469,7 @@ func (p *parser) snippetTokens() ([]Token, error) {
}
}
if p.Val() == "{" {
+ p.nesting++
count++
}
tokens = append(tokens, p.tokens[p.cursor])
@@ -490,3 +480,43 @@ func (p *parser) snippetTokens() ([]Token, error) {
}
return tokens, nil
}
+
+// ServerBlock associates any number of keys from the
+// head of the server block with tokens, which are
+// grouped by segments.
+type ServerBlock struct {
+ Keys []string
+ Segments []Segment
+}
+
+// DispenseDirective returns a dispenser that contains
+// all the tokens in the server block.
+func (sb ServerBlock) DispenseDirective(dir string) *Dispenser {
+ var tokens []Token
+ for _, seg := range sb.Segments {
+ if len(seg) > 0 && seg[0].Text == dir {
+ tokens = append(tokens, seg...)
+ }
+ }
+ return NewDispenser("", tokens)
+}
+
+// Segment is a list of tokens which begins with a directive
+// and ends at the end of the directive (either at the end of
+// the line, or at the end of a block it opens).
+type Segment []Token
+
+// Directive returns the directive name for the segment.
+// The directive name is the text of the first token.
+func (s Segment) Directive() string {
+ if len(s) > 0 {
+ return s[0].Text
+ }
+ return ""
+}
+
+// NewDispenser returns a dispenser for this
+// segment's tokens.
+func (s Segment) NewDispenser() *Dispenser {
+ return NewDispenser("", s)
+}