vendor/github.com/containerd/containerd/filters/parser.go
7acea2a2
 package filters
 
 import (
 	"fmt"
 	"io"
 
 	"github.com/containerd/containerd/errdefs"
 	"github.com/pkg/errors"
 )
 
 /*
 Parse the strings into a filter that may be used with an adaptor.
 
 The filter is made up of zero or more selectors.
 
 The format is a comma separated list of expressions, in the form of
 `<fieldpath><op><value>`, known as selectors. All selectors must match the
 target object for the filter to be true.
 
 We define the operators "==" for equality, "!=" for not equal and "~=" for a
 regular expression. If the operator and value are not present, the matcher will
 test for the presence of a value, as defined by the target object.
 
 The formal grammar is as follows:
 
 selectors := selector ("," selector)*
 selector  := fieldpath (operator value)
 fieldpath := field ('.' field)*
 field     := quoted | [A-Za-z] [A-Za-z0-9_]+
 operator  := "==" | "!=" | "~="
 value     := quoted | [^\s,]+
 quoted    := <go string syntax>
 
 */
 func Parse(s string) (Filter, error) {
 	// special case empty to match all
 	if s == "" {
 		return Always, nil
 	}
 
 	p := parser{input: s}
 	return p.parse()
 }
 
 // ParseAll parses each filter in ss and returns a filter that will return true
 // if any filter matches the expression.
 //
 // If no filters are provided, the filter will match anything.
 func ParseAll(ss ...string) (Filter, error) {
 	if len(ss) == 0 {
 		return Always, nil
 	}
 
 	var fs []Filter
 	for _, s := range ss {
 		f, err := Parse(s)
 		if err != nil {
 			return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error())
 		}
 
 		fs = append(fs, f)
 	}
 
 	return Any(fs), nil
 }
 
 type parser struct {
 	input   string
 	scanner scanner
 }
 
 func (p *parser) parse() (Filter, error) {
 	p.scanner.init(p.input)
 
 	ss, err := p.selectors()
 	if err != nil {
 		return nil, errors.Wrap(err, "filters")
 	}
 
 	return ss, nil
 }
 
 func (p *parser) selectors() (Filter, error) {
 	s, err := p.selector()
 	if err != nil {
 		return nil, err
 	}
 
 	ss := All{s}
 
 loop:
 	for {
 		tok := p.scanner.peek()
 		switch tok {
 		case ',':
 			pos, tok, _ := p.scanner.scan()
 			if tok != tokenSeparator {
 				return nil, p.mkerr(pos, "expected a separator")
 			}
 
 			s, err := p.selector()
 			if err != nil {
 				return nil, err
 			}
 
 			ss = append(ss, s)
 		case tokenEOF:
 			break loop
 		default:
 			return nil, p.mkerr(p.scanner.ppos, "unexpected input: %v", string(tok))
 		}
 	}
 
 	return ss, nil
 }
 
 func (p *parser) selector() (selector, error) {
 	fieldpath, err := p.fieldpath()
 	if err != nil {
 		return selector{}, err
 	}
 
 	switch p.scanner.peek() {
 	case ',', tokenSeparator, tokenEOF:
 		return selector{
 			fieldpath: fieldpath,
 			operator:  operatorPresent,
 		}, nil
 	}
 
 	op, err := p.operator()
 	if err != nil {
 		return selector{}, err
 	}
 
18a53d03
 	var allowAltQuotes bool
 	if op == operatorMatches {
 		allowAltQuotes = true
 	}
 
 	value, err := p.value(allowAltQuotes)
7acea2a2
 	if err != nil {
 		if err == io.EOF {
 			return selector{}, io.ErrUnexpectedEOF
 		}
 		return selector{}, err
 	}
 
 	return selector{
 		fieldpath: fieldpath,
 		value:     value,
 		operator:  op,
 	}, nil
 }
 
 func (p *parser) fieldpath() ([]string, error) {
 	f, err := p.field()
 	if err != nil {
 		return nil, err
 	}
 
 	fs := []string{f}
 loop:
 	for {
 		tok := p.scanner.peek() // lookahead to consume field separator
 
 		switch tok {
 		case '.':
 			pos, tok, _ := p.scanner.scan() // consume separator
 			if tok != tokenSeparator {
 				return nil, p.mkerr(pos, "expected a field separator (`.`)")
 			}
 
 			f, err := p.field()
 			if err != nil {
 				return nil, err
 			}
 
 			fs = append(fs, f)
 		default:
 			// let the layer above handle the other bad cases.
 			break loop
 		}
 	}
 
 	return fs, nil
 }
 
 func (p *parser) field() (string, error) {
 	pos, tok, s := p.scanner.scan()
 	switch tok {
 	case tokenField:
 		return s, nil
 	case tokenQuoted:
18a53d03
 		return p.unquote(pos, s, false)
7acea2a2
 	}
 
 	return "", p.mkerr(pos, "expected field or quoted")
 }
 
 func (p *parser) operator() (operator, error) {
 	pos, tok, s := p.scanner.scan()
 	switch tok {
 	case tokenOperator:
 		switch s {
 		case "==":
 			return operatorEqual, nil
 		case "!=":
 			return operatorNotEqual, nil
 		case "~=":
 			return operatorMatches, nil
 		default:
 			return 0, p.mkerr(pos, "unsupported operator %q", s)
 		}
 	}
 
 	return 0, p.mkerr(pos, `expected an operator ("=="|"!="|"~=")`)
 }
 
18a53d03
 func (p *parser) value(allowAltQuotes bool) (string, error) {
7acea2a2
 	pos, tok, s := p.scanner.scan()
 
 	switch tok {
 	case tokenValue, tokenField:
 		return s, nil
 	case tokenQuoted:
18a53d03
 		return p.unquote(pos, s, allowAltQuotes)
7acea2a2
 	}
 
 	return "", p.mkerr(pos, "expected value or quoted")
 }
 
18a53d03
 func (p *parser) unquote(pos int, s string, allowAlts bool) (string, error) {
 	if !allowAlts && s[0] != '\'' && s[0] != '"' {
 		return "", p.mkerr(pos, "invalid quote encountered")
 	}
 
 	uq, err := unquote(s)
7acea2a2
 	if err != nil {
 		return "", p.mkerr(pos, "unquoting failed: %v", err)
 	}
 
 	return uq, nil
 }
 
 type parseError struct {
 	input string
 	pos   int
 	msg   string
 }
 
 func (pe parseError) Error() string {
 	if pe.pos < len(pe.input) {
 		before := pe.input[:pe.pos]
 		location := pe.input[pe.pos : pe.pos+1] // need to handle end
 		after := pe.input[pe.pos+1:]
 
 		return fmt.Sprintf("[%s >|%s|< %s]: %v", before, location, after, pe.msg)
 	}
 
 	return fmt.Sprintf("[%s]: %v", pe.input, pe.msg)
 }
 
 func (p *parser) mkerr(pos int, format string, args ...interface{}) error {
 	return errors.Wrap(parseError{
 		input: p.input,
 		pos:   pos,
 		msg:   fmt.Sprintf(format, args...),
 	}, "parse error")
 }