Remove custom verb support from template parser

Remove support for custom verbs because of the ambiguity with colons in the rest of the path.

Also replace the existing path template parser, which was ad-hoc, with a recursive descent parser.
diff --git a/path_template.go b/path_template.go
index dee552f..7cbf011 100644
--- a/path_template.go
+++ b/path_template.go
@@ -32,14 +32,9 @@
 import (
 	"errors"
 	"fmt"
-	"regexp"
 	"strings"
 )
 
-var (
-	customVerbRegexp = regexp.MustCompile(":([^:/*}{=]+)$")
-)
-
 type matcher interface {
 	match([]string) (int, error)
 	String() string
@@ -103,80 +98,6 @@
 	return fmt.Sprintf("at %d of template '%s', %s", pe.Pos, pe.Template, pe.Message)
 }
 
-func parseSegments(template string) ([]segment, error) {
-	if len(template) == 0 {
-		return nil, ParseError{0, template, "input is empty"}
-	}
-	var pathWildcardFound bool
-	var segments []segment
-	paths := strings.Split(template, "/")
-	unnamedVariableCount := 0
-	nameSet := map[string]struct{}{}
-	charPos := 0
-	var currentVarName string
-	for i, path := range paths {
-		// Empty path with i == 0 should be allowed for the templates starting with '/'.
-		if path == "" && i != 0 {
-			return nil, ParseError{charPos, template, "empty path component"}
-		}
-		var matcher matcher
-		name := currentVarName
-		if strings.HasPrefix(path, "{") {
-			equalPos := strings.Index(path, "=")
-			if equalPos > 0 {
-				name = path[1:equalPos]
-				path = path[equalPos+1:]
-				if currentVarName != "" {
-					return nil, ParseError{charPos, template, "recursive named bindings are not allowed"}
-				}
-				currentVarName = name
-			} else {
-				if path[len(path)-1] != '}' {
-					return nil, ParseError{charPos, template, "'}' is expected"}
-				}
-				if currentVarName != "" {
-					return nil, ParseError{charPos, template, "recursive named bindings are not allowed"}
-				}
-				name = path[1 : len(path)-1]
-				path = "*"
-			}
-			if _, ok := nameSet[name]; ok {
-				return nil, ParseError{charPos, template, fmt.Sprintf("%s appears multiple times", name)}
-			}
-			nameSet[name] = struct{}{}
-		}
-		if strings.HasPrefix(path, "}") {
-			return nil, ParseError{charPos, template, "} is not allowed here"}
-		}
-		if strings.HasSuffix(path, "}") {
-			path = path[:len(path)-1]
-			currentVarName = ""
-		}
-		if path == "*" {
-			if name == "" {
-				name = fmt.Sprintf("$%d", unnamedVariableCount)
-				unnamedVariableCount++
-			}
-			matcher = wildcardMatcher(0)
-		} else if path == "**" {
-			if pathWildcardFound {
-				return nil, ParseError{charPos, template, "multiple ** isn't allowed"}
-			}
-			pathWildcardFound = true
-			if name == "" {
-				name = fmt.Sprintf("$%d", unnamedVariableCount)
-				unnamedVariableCount++
-			}
-			matcher = pathWildcardMatcher(len(paths) - i - 1)
-		} else {
-			matcher = labelMatcher(path)
-		}
-		segments = append(segments, segment{matcher, name})
-		charPos += len(path) + 1
-	}
-	return segments, nil
-}
-
 // PathTemplate manages the template to build and match with paths used
 // by API services. It holds a template and variable names in it, and
 // it can extract matched patterns from a path string or build a path
@@ -185,27 +106,13 @@
 // See http.proto in github.com/googleapis/googleapis/ for the details of
 // the template syntax.
 type PathTemplate struct {
-	segments   []segment
-	customVerb string
-}
-
-func getCustomVerb(path string) (main string, customVerb string) {
-	matched := customVerbRegexp.FindStringSubmatchIndex(path)
-	if len(matched) == 0 {
-		return path, ""
-	}
-	return path[:matched[0]], path[matched[2]:]
+	segments []segment
 }
 
 // NewPathTemplate parses a path template, and returns a PathTemplate
 // instance if successful.
 func NewPathTemplate(template string) (*PathTemplate, error) {
-	template, customVerb := getCustomVerb(template)
-	segments, err := parseSegments(template)
-	if err != nil {
-		return nil, err
-	}
-	return &PathTemplate{segments: segments, customVerb: customVerb}, nil
+	return parsePathTemplate(template)
 }
 
 // MustCompilePathTemplate is like NewPathTemplate but panics if the
@@ -222,10 +129,6 @@
 // Match attempts to match the given path with the template, and returns
 // the mapping of the variable name to the matched pattern string.
 func (pt *PathTemplate) Match(path string) (map[string]string, error) {
-	path, customVerb := getCustomVerb(path)
-	if pt.customVerb != customVerb {
-		return nil, errors.New("custom verb doesn't match")
-	}
 	paths := strings.Split(path, "/")
 	values := map[string]string{}
 	for _, segment := range pt.segments {
@@ -269,8 +172,5 @@
 		}
 	}
 	built := strings.Join(result, "/")
-	if pt.customVerb != "" {
-		built += ":" + pt.customVerb
-	}
 	return built, nil
 }
diff --git a/path_template_parser.go b/path_template_parser.go
new file mode 100644
index 0000000..f49a571
--- /dev/null
+++ b/path_template_parser.go
@@ -0,0 +1,198 @@
+package gax
+
+import (
+	"fmt"
+	"io"
+	"strings"
+)
+
+// This parser follows the syntax of path templates, from
+// https://github.com/googleapis/googleapis/blob/master/google/api/http.proto.
+// The differences are that there is no custom verb, we allow the initial slash
+// to be absent, and that we are not strict as
+// https://tools.ietf.org/html/rfc6570 about the characters in identifiers and
+// literals.
+
+type pathTemplateParser struct {
+	r                *strings.Reader
+	runeCount        int             // the number of the current rune in the original string
+	nextVar          int             // the number to use for the next unnamed variable
+	seenName         map[string]bool // names we've seen already
+	seenPathWildcard bool            // have we seen "**" already?
+}
+
+func parsePathTemplate(template string) (pt *PathTemplate, err error) {
+	p := &pathTemplateParser{
+		r:        strings.NewReader(template),
+		seenName: map[string]bool{},
+	}
+
+	// Handle panics with strings like errors.
+	// See pathTemplateParser.error, below.
+	defer func() {
+		if x := recover(); x != nil {
+			errmsg, ok := x.(errString)
+			if !ok {
+				panic(x)
+			}
+			pt = nil
+			err = ParseError{p.runeCount, template, string(errmsg)}
+		}
+	}()
+
+	segs := p.template()
+	// If there is a path wildcard, set its length. We can't do this
+	// until we know how many segments we've got all together.
+	for i, seg := range segs {
+		if _, ok := seg.matcher.(pathWildcardMatcher); ok {
+			segs[i].matcher = pathWildcardMatcher(len(segs) - i - 1)
+			break
+		}
+	}
+	return &PathTemplate{segments: segs}, nil
+
+}
+
+// Used to indicate errors "thrown" by this parser. We don't use string because
+// many parts of the standard library panic with strings.
+type errString string
+
+// Terminates parsing immediately with an error.
+func (p *pathTemplateParser) error(msg string) {
+	panic(errString(msg))
+}
+
+// Template = [ "/" ] Segments
+func (p *pathTemplateParser) template() []segment {
+	var segs []segment
+	if p.consume('/') {
+		// Initial '/' needs an initial empty matcher.
+		segs = append(segs, segment{matcher: labelMatcher("")})
+	}
+	return append(segs, p.segments("")...)
+}
+
+// Segments = Segment { "/" Segment }
+func (p *pathTemplateParser) segments(name string) []segment {
+	var segs []segment
+	for {
+		subsegs := p.segment(name)
+		segs = append(segs, subsegs...)
+		if !p.consume('/') {
+			break
+		}
+	}
+	return segs
+}
+
+// Segment  = "*" | "**" | LITERAL | Variable
+func (p *pathTemplateParser) segment(name string) []segment {
+	if p.consume('*') {
+		if name == "" {
+			name = fmt.Sprintf("$%d", p.nextVar)
+			p.nextVar++
+		}
+		if p.consume('*') {
+			if p.seenPathWildcard {
+				p.error("multiple '**' disallowed")
+			}
+			p.seenPathWildcard = true
+			// We'll change 0 to the right number at the end.
+			return []segment{{name: name, matcher: pathWildcardMatcher(0)}}
+		}
+		return []segment{{name: name, matcher: wildcardMatcher(0)}}
+	}
+	if p.consume('{') {
+		if name != "" {
+			p.error("recursive named bindings are not allowed")
+		}
+		return p.variable()
+	}
+	return []segment{{name: name, matcher: labelMatcher(p.literal())}}
+}
+
+// Variable = "{" FieldPath [ "=" Segments ] "}"
+// "{" is already consumed.
+func (p *pathTemplateParser) variable() []segment {
+	// Simplification: treat FieldPath as LITERAL, instead of IDENT { '.' IDENT }
+	name := p.literal()
+	if p.seenName[name] {
+		p.error(name + " appears multiple times")
+	}
+	p.seenName[name] = true
+	var segs []segment
+	if p.consume('=') {
+		segs = p.segments(name)
+	} else {
+		// "{var}" is equivalent to "{var=*}"
+		segs = []segment{{name: name, matcher: wildcardMatcher(0)}}
+	}
+	if !p.consume('}') {
+		p.error("expected '}'")
+	}
+	return segs
+}
+
+// A literal is any sequence of characters other than a few special ones.
+// The list of stop characters is not quite the same as in the template RFC.
+func (p *pathTemplateParser) literal() string {
+	lit := p.consumeUntil("/*}{=")
+	if lit == "" {
+		p.error("empty literal")
+	}
+	return lit
+}
+
+// Read runes until EOF or one of the runes in stopRunes is encountered.
+// If the latter, unread the stop rune. Return the accumulated runes as a string.
+func (p *pathTemplateParser) consumeUntil(stopRunes string) string {
+	var runes []rune
+	for {
+		r, ok := p.readRune()
+		if !ok {
+			break
+		}
+		if strings.IndexRune(stopRunes, r) >= 0 {
+			p.unreadRune()
+			break
+		}
+		runes = append(runes, r)
+	}
+	return string(runes)
+}
+
+// If the next rune is r, consume it and return true.
+// Otherwise, leave the input unchanged and return false.
+func (p *pathTemplateParser) consume(r rune) bool {
+	rr, ok := p.readRune()
+	if !ok {
+		return false
+	}
+	if r == rr {
+		return true
+	}
+	p.unreadRune()
+	return false
+}
+
+// Read the next rune from the input. Return it.
+// The second return value is false at EOF.
+func (p *pathTemplateParser) readRune() (rune, bool) {
+	r, _, err := p.r.ReadRune()
+	if err == io.EOF {
+		return r, false
+	}
+	if err != nil {
+		p.error(err.Error())
+	}
+	p.runeCount++
+	return r, true
+}
+
+// Put the last rune that was read back on the input.
+func (p *pathTemplateParser) unreadRune() {
+	if err := p.r.UnreadRune(); err != nil {
+		p.error(err.Error())
+	}
+	p.runeCount--
+}
diff --git a/path_template_test.go b/path_template_test.go
index ddd929b..09b598f 100644
--- a/path_template_test.go
+++ b/path_template_test.go
@@ -45,12 +45,6 @@
 			map[string]string{"$0": "f", "$1": "o", "$2": "bar"},
 		},
 		{
-			"custom verb",
-			"buckets/*/objects/*:custom",
-			"buckets/f/objects/o:custom",
-			map[string]string{"$0": "f", "$1": "o"},
-		},
-		{
 			"path wildcards",
 			"bar/**/foo/*",
 			"bar/foo/foo/foo/bar",
@@ -63,6 +57,12 @@
 			map[string]string{"$0": "bar", "foo": "foo"},
 		},
 		{
+			"named binding with colon",
+			"buckets/{foo}/objects/*",
+			"buckets/foo:boo/objects/bar",
+			map[string]string{"$0": "bar", "foo": "foo:boo"},
+		},
+		{
 			"named binding with complex patterns",
 			"buckets/{foo=x/*/y/**}/objects/*",
 			"buckets/x/foo/y/bar/baz/objects/quox",
@@ -128,11 +128,6 @@
 			"buckets/*/*/objects/*",
 			"buckets/f/o/objects/too/long",
 		},
-		{
-			"wrong custom verb",
-			"buckets/*/objects/*:bar",
-			"buckets/foo/objects/bar:bazz",
-		},
 	}
 	for _, testCase := range testCases {
 		pt, err := NewPathTemplate(testCase.template)
@@ -203,6 +198,10 @@
 			"same name multiple times",
 			"foo/{foo}/bar/{foo}",
 		},
+		{
+			"empty string after '='",
+			"foo/{foo=}/bar",
+		},
 	}
 	for _, testCase := range testCases {
 		if pt, err := NewPathTemplate(testCase.template); err == nil {