diff --git a/vendor/github.com/drone/envsubst/parse/node.go b/vendor/github.com/drone/envsubst/parse/node.go new file mode 100644 index 000000000..09787eb92 --- /dev/null +++ b/vendor/github.com/drone/envsubst/parse/node.go @@ -0,0 +1,86 @@ +package parse + +// Node is an element in the parse tree. +type Node interface { + node() +} + +// empty string node +var empty = new(TextNode) + +// a template is represented by a tree consisting of one +// or more of the following nodes. +type ( + // TextNode represents a string of text. + TextNode struct { + Value string + } + + // FuncNode represents a string function. + FuncNode struct { + Param string + Name string + Args []Node + } + + // ListNode represents a list of nodes. + ListNode struct { + Nodes []Node + } + + // ParamNode struct{ + // Name string + // } + // + // CaseNode struct { + // Name string + // First bool + // } + // + // LowerNode struct { + // Name string + // First bool + // } + // + // SubstrNode struct { + // Name string + // Pos Node + // Len Node + // } + // + // ReplaceNode struct { + // Name string + // Substring Node + // Replacement Node + // } + // + // TrimNode struct{ + // + // } + // + // DefaultNode struct { + // Name string + // Default Node + // } +) + +// newTextNode returns a new TextNode. +func newTextNode(text string) *TextNode { + return &TextNode{Value: text} +} + +// newListNode returns a new ListNode. +func newListNode(nodes ...Node) *ListNode { + return &ListNode{Nodes: nodes} +} + +// newFuncNode returns a new FuncNode. +func newFuncNode(name string) *FuncNode { + return &FuncNode{Param: name} +} + +// node() defines the node in a parse tree + +func (*TextNode) node() {} +func (*ListNode) node() {} +func (*FuncNode) node() {} diff --git a/vendor/github.com/drone/envsubst/parse/parse.go b/vendor/github.com/drone/envsubst/parse/parse.go new file mode 100644 index 000000000..f6ab3bbf2 --- /dev/null +++ b/vendor/github.com/drone/envsubst/parse/parse.go @@ -0,0 +1,371 @@ +package parse + +import "errors" + +// ErrBadSubstitution represents a substitution parsing error. +var ErrBadSubstitution = errors.New("bad substitution") + +// Tree is the representation of a single parsed SQL statement. +type Tree struct { + Root Node + + // Parsing only; cleared after parse. + scanner *scanner +} + +// Parse parses the string and returns a Tree. +func Parse(buf string) (*Tree, error) { + t := new(Tree) + t.scanner = new(scanner) + return t.Parse(buf) +} + +// Parse parses the string buffer to construct an ast +// representation for expansion. +func (t *Tree) Parse(buf string) (tree *Tree, err error) { + t.scanner.init(buf) + t.Root, err = t.parseAny() + return t, err +} + +func (t *Tree) parseAny() (Node, error) { + t.scanner.accept = acceptRune + t.scanner.mode = scanIdent | scanLbrack + + switch t.scanner.scan() { + case tokenIdent: + left := newTextNode( + t.scanner.string(), + ) + right, err := t.parseAny() + switch { + case err != nil: + return nil, err + case right == empty: + return left, nil + } + return newListNode(left, right), nil + case tokenEOF: + return empty, nil + case tokenLbrack: + left, err := t.parseFunc() + if err != nil { + return nil, err + } + + right, err := t.parseAny() + switch { + case err != nil: + return nil, err + case right == empty: + return left, nil + } + return newListNode(left, right), nil + } + + return nil, ErrBadSubstitution +} + +func (t *Tree) parseFunc() (Node, error) { + switch t.scanner.peek() { + case '#': + return t.parseLenFunc() + } + + var name string + t.scanner.accept = acceptIdent + t.scanner.mode = scanIdent + + switch t.scanner.scan() { + case tokenIdent: + name = t.scanner.string() + default: + return nil, ErrBadSubstitution + } + + switch t.scanner.peek() { + case ':': + return t.parseDefaultOrSubstr(name) + case '=': + return t.parseDefaultFunc(name) + case ',', '^': + return t.parseCasingFunc(name) + case '/': + return t.parseReplaceFunc(name) + case '#': + return t.parseRemoveFunc(name, acceptHashFunc) + case '%': + return t.parseRemoveFunc(name, acceptPercentFunc) + } + + t.scanner.accept = acceptIdent + t.scanner.mode = scanRbrack + switch t.scanner.scan() { + case tokenRbrack: + return newFuncNode(name), nil + default: + return nil, ErrBadSubstitution + } +} + +// parse a substitution function parameter. +func (t *Tree) parseParam(accept acceptFunc, mode byte) (Node, error) { + t.scanner.accept = accept + t.scanner.mode = mode | scanLbrack + switch t.scanner.scan() { + case tokenLbrack: + return t.parseFunc() + case tokenIdent: + return newTextNode( + t.scanner.string(), + ), nil + default: + return nil, ErrBadSubstitution + } +} + +// parse either a default or substring substitution function. +func (t *Tree) parseDefaultOrSubstr(name string) (Node, error) { + t.scanner.read() + r := t.scanner.peek() + t.scanner.unread() + switch r { + case '=', '-', '?', '+': + return t.parseDefaultFunc(name) + default: + return t.parseSubstrFunc(name) + } +} + +// parses the ${param:offset} string function +// parses the ${param:offset:length} string function +func (t *Tree) parseSubstrFunc(name string) (Node, error) { + node := new(FuncNode) + node.Param = name + + t.scanner.accept = acceptOneColon + t.scanner.mode = scanIdent + switch t.scanner.scan() { + case tokenIdent: + node.Name = t.scanner.string() + default: + return nil, ErrBadSubstitution + } + + // scan arg[1] + { + param, err := t.parseParam(rejectColonClose, scanIdent) + if err != nil { + return nil, err + } + + // param.Value = t.scanner.string() + node.Args = append(node.Args, param) + } + + // expect delimiter or close + t.scanner.accept = acceptColon + t.scanner.mode = scanIdent | scanRbrack + switch t.scanner.scan() { + case tokenRbrack: + return node, nil + case tokenIdent: + // no-op + default: + return nil, ErrBadSubstitution + } + // err := t.consumeDelimiter(acceptColon, scanIdent|scanRbrack) + // if err != nil { + // return nil, err + // } + + // scan arg[2] + { + param, err := t.parseParam(acceptNotClosing, scanIdent) + if err != nil { + return nil, err + } + node.Args = append(node.Args, param) + } + + return node, t.consumeRbrack() +} + +// parses the ${param%word} string function +// parses the ${param%%word} string function +// parses the ${param#word} string function +// parses the ${param##word} string function +func (t *Tree) parseRemoveFunc(name string, accept acceptFunc) (Node, error) { + node := new(FuncNode) + node.Param = name + + t.scanner.accept = accept + t.scanner.mode = scanIdent + switch t.scanner.scan() { + case tokenIdent: + node.Name = t.scanner.string() + default: + return nil, ErrBadSubstitution + } + + // scan arg[1] + { + param, err := t.parseParam(acceptNotClosing, scanIdent) + if err != nil { + return nil, err + } + + // param.Value = t.scanner.string() + node.Args = append(node.Args, param) + } + + return node, t.consumeRbrack() +} + +// parses the ${param/pattern/string} string function +// parses the ${param//pattern/string} string function +// parses the ${param/#pattern/string} string function +// parses the ${param/%pattern/string} string function +func (t *Tree) parseReplaceFunc(name string) (Node, error) { + node := new(FuncNode) + node.Param = name + + t.scanner.accept = acceptReplaceFunc + t.scanner.mode = scanIdent + switch t.scanner.scan() { + case tokenIdent: + node.Name = t.scanner.string() + default: + return nil, ErrBadSubstitution + } + + // scan arg[1] + { + param, err := t.parseParam(acceptNotSlash, scanIdent|scanEscape) + if err != nil { + return nil, err + } + node.Args = append(node.Args, param) + } + + // expect delimiter + t.scanner.accept = acceptSlash + t.scanner.mode = scanIdent + switch t.scanner.scan() { + case tokenIdent: + // no-op + default: + return nil, ErrBadSubstitution + } + + { + param, err := t.parseParam(acceptNotClosing, scanIdent) + if err != nil { + return nil, err + } + node.Args = append(node.Args, param) + } + + return node, t.consumeRbrack() +} + +// parses the ${parameter=word} string function +// parses the ${parameter:=word} string function +// parses the ${parameter:-word} string function +// parses the ${parameter:?word} string function +// parses the ${parameter:+word} string function +func (t *Tree) parseDefaultFunc(name string) (Node, error) { + node := new(FuncNode) + node.Param = name + + t.scanner.accept = acceptDefaultFunc + if t.scanner.peek() == '=' { + t.scanner.accept = acceptOneEqual + } + t.scanner.mode = scanIdent + switch t.scanner.scan() { + case tokenIdent: + node.Name = t.scanner.string() + default: + return nil, ErrBadSubstitution + } + + // scan arg[1] + { + param, err := t.parseParam(acceptNotClosing, scanIdent) + if err != nil { + return nil, err + } + + // param.Value = t.scanner.string() + node.Args = append(node.Args, param) + } + + return node, t.consumeRbrack() +} + +// parses the ${param,} string function +// parses the ${param,,} string function +// parses the ${param^} string function +// parses the ${param^^} string function +func (t *Tree) parseCasingFunc(name string) (Node, error) { + node := new(FuncNode) + node.Param = name + + t.scanner.accept = acceptCasingFunc + t.scanner.mode = scanIdent + switch t.scanner.scan() { + case tokenIdent: + node.Name = t.scanner.string() + default: + return nil, ErrBadSubstitution + } + + return node, t.consumeRbrack() +} + +// parses the ${#param} string function +func (t *Tree) parseLenFunc() (Node, error) { + node := new(FuncNode) + + t.scanner.accept = acceptOneHash + t.scanner.mode = scanIdent + switch t.scanner.scan() { + case tokenIdent: + node.Name = t.scanner.string() + default: + return nil, ErrBadSubstitution + } + + t.scanner.accept = acceptIdent + t.scanner.mode = scanIdent + switch t.scanner.scan() { + case tokenIdent: + node.Param = t.scanner.string() + default: + return nil, ErrBadSubstitution + } + + return node, t.consumeRbrack() +} + +// consumeRbrack consumes a right closing bracket. If a closing +// bracket token is not consumed an ErrBadSubstitution is returned. +func (t *Tree) consumeRbrack() error { + t.scanner.mode = scanRbrack + if t.scanner.scan() != tokenRbrack { + return ErrBadSubstitution + } + return nil +} + +// consumeDelimiter consumes a function argument delimiter. If a +// delimiter is not consumed an ErrBadSubstitution is returned. +// func (t *Tree) consumeDelimiter(accept acceptFunc, mode uint) error { +// t.scanner.accept = accept +// t.scanner.mode = mode +// if t.scanner.scan() != tokenRbrack { +// return ErrBadSubstitution +// } +// return nil +// } diff --git a/vendor/github.com/drone/envsubst/parse/scan.go b/vendor/github.com/drone/envsubst/parse/scan.go new file mode 100644 index 000000000..c598e9bc8 --- /dev/null +++ b/vendor/github.com/drone/envsubst/parse/scan.go @@ -0,0 +1,275 @@ +package parse + +import ( + "unicode" + "unicode/utf8" +) + +// eof rune sent when end of file is reached +var eof = rune(0) + +// token is a lexical token. +type token uint + +// list of lexical tokens. +const ( + // special tokens + tokenIllegal token = iota + tokenEOF + + // identifiers and literals + tokenIdent + + // operators and delimiters + tokenLbrack + tokenRbrack + tokenQuote +) + +// predefined mode bits to control recognition of tokens. +const ( + scanIdent byte = 1 << iota + scanLbrack + scanRbrack + scanEscape +) + +// returns true if rune is accepted. +type acceptFunc func(r rune, i int) bool + +// scanner implements a lexical scanner that reads unicode +// characters and tokens from a string buffer. +type scanner struct { + buf string + pos int + start int + width int + mode byte + + accept acceptFunc +} + +// init initializes a scanner with a new buffer. +func (s *scanner) init(buf string) { + s.buf = buf + s.pos = 0 + s.start = 0 + s.width = 0 + s.accept = nil +} + +// read returns the next unicode character. It returns eof at +// the end of the string buffer. +func (s *scanner) read() rune { + if s.pos >= len(s.buf) { + s.width = 0 + return eof + } + r, w := utf8.DecodeRuneInString(s.buf[s.pos:]) + s.width = w + s.pos += s.width + return r +} + +func (s *scanner) unread() { + s.pos -= s.width +} + +// skip skips over the curring unicode character in the buffer +// by slicing and removing from the buffer. +func (s *scanner) skip() { + l := s.buf[:s.pos-1] + r := s.buf[s.pos:] + s.buf = l + r +} + +// peek returns the next unicode character in the buffer without +// advancing the scanner. It returns eof if the scanner's position +// is at the last character of the source. +func (s *scanner) peek() rune { + r := s.read() + s.unread() + return r +} + +// string returns the string corresponding to the most recently +// scanned token. Valid after calling scan(). +func (s *scanner) string() string { + return s.buf[s.start:s.pos] +} + +// scan reads the next token or Unicode character from source and +// returns it. It returns EOF at the end of the source. +func (s *scanner) scan() token { + s.start = s.pos + r := s.read() + switch { + case r == eof: + return tokenEOF + case s.scanLbrack(r): + return tokenLbrack + case s.scanRbrack(r): + return tokenRbrack + case s.scanIdent(r): + return tokenIdent + } + return tokenIllegal +} + +// scanIdent reads the next token or Unicode character from source +// and returns true if the Ident character is accepted. +func (s *scanner) scanIdent(r rune) bool { + if s.mode&scanIdent == 0 { + return false + } + if s.scanEscaped(r) { + s.skip() + } else if !s.accept(r, s.pos-s.start) { + return false + } +loop: + for { + r := s.read() + switch { + case r == eof: + s.unread() + break loop + case s.scanLbrack(r): + s.unread() + s.unread() + break loop + } + if s.scanEscaped(r) { + s.skip() + continue + } + if !s.accept(r, s.pos-s.start) { + s.unread() + break loop + } + } + return true +} + +// scanLbrack reads the next token or Unicode character from source +// and returns true if the open bracket is encountered. +func (s *scanner) scanLbrack(r rune) bool { + if s.mode&scanLbrack == 0 { + return false + } + if r == '$' { + if s.read() == '{' { + return true + } + s.unread() + } + return false +} + +// scanRbrack reads the next token or Unicode character from source +// and returns true if the closing bracket is encountered. +func (s *scanner) scanRbrack(r rune) bool { + if s.mode&scanRbrack == 0 { + return false + } + return r == '}' +} + +// scanEscaped reads the next token or Unicode character from source +// and returns true if it being escaped and should be sipped. +func (s *scanner) scanEscaped(r rune) bool { + if s.mode&scanEscape == 0 { + return false + } + if r != '\\' { + return false + } + switch s.peek() { + case '/', '\\': + return true + default: + return false + } +} + +// +// scanner functions accept or reject runes. +// + +func acceptRune(r rune, i int) bool { + return true +} + +func acceptIdent(r rune, i int) bool { + return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' +} + +func acceptColon(r rune, i int) bool { + return r == ':' +} + +func acceptOneHash(r rune, i int) bool { + return r == '#' && i == 1 +} + +func acceptNone(r rune, i int) bool { + return false +} + +func acceptNotClosing(r rune, i int) bool { + return r != '}' +} + +func acceptHashFunc(r rune, i int) bool { + return r == '#' && i < 3 +} + +func acceptPercentFunc(r rune, i int) bool { + return r == '%' && i < 3 +} + +func acceptDefaultFunc(r rune, i int) bool { + switch { + case i == 1 && r == ':': + return true + case i == 2 && (r == '=' || r == '-' || r == '?' || r == '+'): + return true + default: + return false + } +} + +func acceptReplaceFunc(r rune, i int) bool { + switch { + case i == 1 && r == '/': + return true + case i == 2 && (r == '/' || r == '#' || r == '%'): + return true + default: + return false + } +} + +func acceptOneEqual(r rune, i int) bool { + return i == 1 && r == '=' +} + +func acceptOneColon(r rune, i int) bool { + return i == 1 && r == ':' +} + +func rejectColonClose(r rune, i int) bool { + return r != ':' && r != '}' +} + +func acceptSlash(r rune, i int) bool { + return r == '/' +} + +func acceptNotSlash(r rune, i int) bool { + return r != '/' +} + +func acceptCasingFunc(r rune, i int) bool { + return (r == ',' || r == '^') && i < 3 +} diff --git a/vendor/vendor.json b/vendor/vendor.json index e5459cea2..2102f5ba1 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -59,6 +59,12 @@ "revision": "3e65ae5fd2d944d56fdf52cb3f887247498d50e9", "revisionTime": "2017-01-18T15:01:55Z" }, + { + "checksumSHA1": "LPKfsjm4AYVgCLVcAWnQdSpt5SA=", + "path": "github.com/drone/envsubst/parse", + "revision": "3e65ae5fd2d944d56fdf52cb3f887247498d50e9", + "revisionTime": "2017-01-18T15:01:55Z" + }, { "path": "github.com/eknkc/amber", "revision": "144da19a9994994c069f0693294a66dd310e14a4",