updated vendor

This commit is contained in:
ston1th 2019-08-29 19:30:16 +02:00
commit 5e9ce5a306
8 changed files with 1306 additions and 5 deletions

621
vendor/github.com/bcampbell/qs/parse.go generated vendored Normal file
View file

@ -0,0 +1,621 @@
package qs
import (
"fmt"
"github.com/blevesearch/bleve"
"github.com/blevesearch/bleve/search/query"
"strconv"
"strings"
"time"
)
// ParseError is the error type returned by Parse()
type ParseError struct {
// Pos is the character position where the error occured
Pos int
// Msg is a description of the error
Msg string
}
func (pe ParseError) Error() string { return fmt.Sprintf("%d: %s", pe.Pos, pe.Msg) }
type OpType int
const (
OR OpType = 0
AND = 1
)
type Parser struct {
tokens []token
pos int
// DefaultOp is used when no explict OR or AND is present
// ie: foo bar => foo OR bar | foo AND bar
// TODO: not sure AND/OR is the right terminology (but it's what others use)
// Doesn't actually use OR or AND. AND is treated as an implied '+' prefix
DefaultOp OpType
// Loc is the location to use for parsing dates in range queries.
// If nil, UTC is assumed.
Loc *time.Location
}
// context is used to hold settings active within a given scope during parsing
// (TODO: maybe this should just be absorbed back into the Parser struct instead?)
type context struct {
// field is the name of the field currently in scope (or "")
field string
fieldPos int
}
// Parse takes a query string and turns it into a bleve Query.
//
// Returned errors are type ParseError, which includes the position
// of the offending part of the input string.
//
// BNF(ish) query syntax:
// exprList = expr1*
// expr1 = expr2 {"OR" expr2}
// expr2 = expr3 {"AND" expr3}
// expr3 = {"NOT"} expr4
// expr4 = {("+"|"-")} expr5
// expr5 = {field} part {boost}
// part = lit {"~" number} | range | "(" exprList ")"
// field = lit ":"
// range = ("["|"}") {lit} "TO" {lit} ("]"|"}")
// relational = ("<"|">"|"<="|">=") lit
// boost = "^" number
//
// (where lit is a string, quoted string or number)
func (p *Parser) Parse(q string) (query.Query, error) {
p.tokens = lex(q)
ctx := context{field: ""}
return p.parseExprList(ctx)
}
// Parse takes a query string and turns it into a bleve Query using
// the default Parser.
// Returned errors are type ParseError, which includes the position
// of the offending part of the input string.
func Parse(q string) (query.Query, error) {
p := Parser{DefaultOp: OR}
return p.Parse(q)
}
// peek looks at the next token without consuming it.
// peeks beyond the end of the token stream will return EOF
func (p *Parser) peek() token {
if p.pos < len(p.tokens) {
tok := p.tokens[p.pos]
return tok
}
return token{typ: tEOF}
}
// backup steps back one position in the token stream
func (p *Parser) backup() {
p.pos -= 1
}
// next fetches the next token in the stream
func (p *Parser) next() token {
if p.pos < len(p.tokens) {
tok := p.tokens[p.pos]
p.pos += 1
return tok
}
p.pos += 1 // to make sure backup() works
return token{typ: tEOF}
}
// starting point
// exprList = expr1*
func (p *Parser) parseExprList(ctx context) (query.Query, error) {
// <empty>
if p.peek().typ == tEOF {
return bleve.NewMatchNoneQuery(), nil
}
must := []query.Query{}
mustNot := []query.Query{}
should := []query.Query{}
for {
tok := p.peek()
if tok.typ == tEOF {
break
}
// slightly kludgy...
if tok.typ == tRPAREN {
break
}
prefix, q, err := p.parseExpr1(ctx)
if err != nil {
return nil, err
}
switch prefix {
case tPLUS:
must = append(must, q)
case tMINUS:
mustNot = append(mustNot, q)
default:
if p.DefaultOp == AND {
must = append(must, q)
} else { // OR
should = append(should, q)
}
}
}
// some obvious shortcuts
total := len(must) + len(mustNot) + len(should)
if total == 0 {
return bleve.NewMatchNoneQuery(), nil
}
if total == 1 && len(must) == 1 {
return must[0], nil
}
if total == 1 && len(should) == 1 {
return should[0], nil
}
// no shortcuts - go with the full-fat version
q := bleve.NewBooleanQuery()
if len(must) > 0 {
q.AddMust(must...)
}
if len(should) > 0 {
q.AddShould(should...)
}
if len(mustNot) > 0 {
q.AddMustNot(mustNot...)
}
return q, nil
}
// parseExpr1 handles OR expressions
//
// expr1 = expr2 {"OR" expr2}
func (p *Parser) parseExpr1(ctx context) (tokType, query.Query, error) {
queries := []query.Query{}
prefixes := []tokType{}
for {
prefix, q, err := p.parseExpr2(ctx)
if err != nil {
return tEOF, nil, err
}
prefixes = append(prefixes, prefix)
queries = append(queries, q)
tok := p.next()
if tok.typ != tOR {
p.backup()
break
}
}
// let single, non-OR expressions bubble upward, prefix intact
if len(queries) == 1 {
return prefixes[0], queries[0], nil
}
// KLUDGINESS - prefixes on terms in OR expressions
// we'll ignore "+" and treat "-" as NOT
// eg:
// `+alice OR -bob OR chuck` => `alice OR (NOT bob) OR chuck`
for i, _ := range queries {
if prefixes[i] == tMINUS {
q := bleve.NewBooleanQuery()
q.AddMustNot(queries[i])
queries[i] = q
}
}
return tEOF, bleve.NewDisjunctionQuery(queries...), nil
}
// parseExpr2 handles AND expressions
//
// expr2 = expr3 {"AND" expr3}
func (p *Parser) parseExpr2(ctx context) (tokType, query.Query, error) {
queries := []query.Query{}
prefixes := []tokType{}
for {
prefix, q, err := p.parseExpr3(ctx)
if err != nil {
return tEOF, nil, err
}
prefixes = append(prefixes, prefix)
queries = append(queries, q)
tok := p.next()
if tok.typ != tAND {
p.backup()
break
}
}
// let single, non-AND expressions bubble upward, prefix intact
if len(queries) == 1 {
return prefixes[0], queries[0], nil
}
// KLUDGINESS - prefixes on terms in AND expressions
// we'll ignore "+" and treat "-" as NOT
// eg:
// `+alice AND -bob AND chuck` => `alice AND (NOT bob) AND chuck`
for i, _ := range queries {
if prefixes[i] == tMINUS {
q := bleve.NewBooleanQuery()
q.AddMustNot(queries[i])
queries[i] = q
}
}
return tEOF, bleve.NewConjunctionQuery(queries...), nil
}
// parseExpr3 handles NOT expressions
//
// expr3 = {"NOT"} expr4
func (p *Parser) parseExpr3(ctx context) (tokType, query.Query, error) {
tok := p.next()
if tok.typ != tNOT {
p.backup()
// just let the lower, non-NOT expression bubble up with its prefix
return p.parseExpr4(ctx)
}
prefix, q, err := p.parseExpr4(ctx)
if err != nil {
return tEOF, nil, err
}
// KLUDGINESS - prefixes on terms in NOT expressions:
// `NOT -bob` => `bob`
// `NOT +bob` => `NOT bob`
if prefix != tMINUS {
q2 := bleve.NewBooleanQuery()
q2.AddMustNot(q)
q = q2
}
return tEOF, q, nil
}
// Here's where all the prefix-bubbling-up begins...
// expr4 = {("+"|"-")} expr5
func (p *Parser) parseExpr4(ctx context) (tokType, query.Query, error) {
var prefix tokType
tok := p.next()
switch tok.typ {
case tMINUS, tPLUS:
prefix = tok.typ
default:
p.backup()
prefix = tEOF
}
q, err := p.parseExpr5(ctx)
return prefix, q, err
}
// expr5 = {field} part {boost}
func (p *Parser) parseExpr5(ctx context) (query.Query, error) {
fldpos := p.peek().pos
fld, err := p.parseField()
if err != nil {
return nil, err
}
if fld != "" {
if ctx.field != "" {
return nil, ParseError{fldpos, fmt.Sprintf("'%s:' clashes with '%s:'", fld, ctx.field)}
}
ctx.field = fld
ctx.fieldPos = fldpos
}
q, err := p.parsePart(ctx)
if err != nil {
return nil, err
}
// parse (optional) suffix
boostpos := p.peek().pos
boost, err := p.parseBoostSuffix()
if err != nil {
return nil, err
}
if boost > 0 {
if boostable, ok := q.(query.BoostableQuery); ok {
boostable.SetBoost(boost)
} else {
return nil, ParseError{boostpos, "can't specify a boost value here"}
}
}
return q, nil
}
// part = lit {"~" number} | range | "(" exprList ")"
func (p *Parser) parsePart(ctx context) (query.Query, error) {
tok := p.next()
// lit
if tok.typ == tLITERAL {
var q query.Query
if strings.ContainsAny(tok.val, "*?") {
q = bleve.NewWildcardQuery(tok.val)
} else {
if p.peek().typ == tFUZZY {
fuzziness, err := p.parseFuzzySuffix()
if err != nil {
return nil, err
}
fuzz := bleve.NewFuzzyQuery(tok.val)
fuzz.SetFuzziness(fuzziness)
q = fuzz
} else {
q = bleve.NewMatchPhraseQuery(tok.val)
}
}
if ctx.field != "" {
if fieldable, ok := q.(query.FieldableQuery); ok {
fieldable.SetField(ctx.field)
} else {
return nil, ParseError{ctx.fieldPos, "unexpected field"}
}
}
return q, nil
}
if tok.typ == tQUOTED {
// strip quotes (ugh)
txt := string(tok.val[1 : len(tok.val)-1])
/*
if strings.ContainsAny(txt, "*?") {
return nil, ParseError{tok.pos, "wildcards not supported in phrases"}
}
*/
q := bleve.NewMatchPhraseQuery(txt)
if ctx.field != "" {
q.SetField(ctx.field)
}
return q, nil
}
// | "(" exprList ")"
if tok.typ == tLPAREN {
q, err := p.parseExprList(ctx)
if err != nil {
return nil, err
}
tok = p.next()
if tok.typ != tRPAREN {
return nil, ParseError{tok.pos, "missing )"}
}
return q, nil
}
// | range
if tok.typ == tLSQUARE || tok.typ == tLBRACE {
p.backup()
q, err := p.parseRange(ctx)
if err != nil {
return nil, err
}
return q, nil
}
// | relational
if tok.typ == tGREATER || tok.typ == tLESS {
p.backup()
q, err := p.parseRelational(ctx)
if err != nil {
return nil, err
}
return q, nil
}
if tok.typ == tERROR {
return nil, ParseError{tok.pos, tok.val}
}
return nil, ParseError{tok.pos, fmt.Sprintf("unexpected %s", tok.val)}
}
// returns >0 if there is a value given
func (p *Parser) parseBoostSuffix() (float64, error) {
tok := p.next()
if tok.typ != tBOOST {
p.backup()
return 0, nil
}
v := tok.val[1:]
if v == "" {
return 1.0, nil
}
boost, err := strconv.ParseFloat(v, 64)
if err != nil {
return 0, ParseError{tok.pos, "bad boost value"}
}
return boost, nil
}
//
func (p *Parser) parseFuzzySuffix() (int, error) {
tok := p.next()
if tok.typ != tFUZZY {
return 0, ParseError{tok.pos, "expected ~"}
}
v := tok.val[1:]
if v == "" {
return 1, nil // Default fuzziness is 1
}
fuzz, err := strconv.Atoi(v)
if err != nil {
return 0, ParseError{tok.pos, "bad fuzziness value"}
}
return fuzz, nil
}
// parse (optional) field specifier
// [ lit ":" ]
// returns field name or "" if not a field
func (p *Parser) parseField() (string, error) {
tok := p.next()
if tok.typ != tLITERAL {
// not a field
p.backup()
return "", nil
}
field := tok.val
tok = p.next()
if tok.typ != tCOLON {
// oop, not a field after all!
p.backup()
p.backup()
return "", nil
}
return field, nil // it's OK
}
// range = ("["|"}") {lit} "TO" {lit} ("]"|"}")
func (p *Parser) parseRange(ctx context) (query.Query, error) {
var minVal, maxVal string
var minInclusive, maxInclusive bool
openTok := p.next()
switch openTok.typ {
case tLSQUARE:
minInclusive = true
case tLBRACE:
minInclusive = false
default:
return nil, ParseError{openTok.pos, "expected range"}
}
tok := p.next()
switch tok.typ {
case tLITERAL:
minVal = tok.val
case tQUOTED:
minVal = tok.val[1 : len(tok.val)-1]
case tTO:
p.backup()
// empty start
default:
return nil, ParseError{tok.pos, fmt.Sprintf("unexpected %s", tok.val)}
}
tok = p.next()
if tok.typ != tTO {
return nil, ParseError{tok.pos, "expected TO"}
}
tok = p.next()
switch tok.typ {
case tLITERAL:
maxVal = tok.val
case tQUOTED:
maxVal = tok.val[1 : len(tok.val)-1]
case tRSQUARE:
p.backup() // empty end value
case tRBRACE:
p.backup() // empty end value
default:
return nil, ParseError{tok.pos, fmt.Sprintf("unexpected %s", tok.val)}
}
closeTok := p.next()
switch closeTok.typ {
case tRSQUARE:
maxInclusive = true
case tRBRACE:
maxInclusive = false
default:
return nil, ParseError{closeTok.pos, "expected ] or }"}
}
rp := newRangeParams(minVal, maxVal, minInclusive, maxInclusive, p.Loc)
q, err := rp.generate()
if err != nil {
return nil, ParseError{openTok.pos, err.Error()}
}
if ctx.field != "" {
if fieldable, ok := q.(query.FieldableQuery); ok {
fieldable.SetField(ctx.field)
} else {
return nil, ParseError{ctx.fieldPos, "unexpected field"}
}
}
return q, nil
}
// parseRelational handles greaterthan/lessthan etc...
// Implemented as a range.
// relational = ("<"|">"|"<="|">=") lit
func (p *Parser) parseRelational(ctx context) (query.Query, error) {
var minVal, maxVal string
var minInclusive, maxInclusive bool
rel := p.next()
if rel.typ != tGREATER && rel.typ != tLESS {
return nil, ParseError{rel.pos, "expected > or <"}
}
eq := p.next()
if eq.typ != tEQUAL {
p.backup()
}
var val string
tok := p.next()
switch tok.typ {
case tLITERAL:
val = tok.val
case tQUOTED:
val = tok.val[1 : len(tok.val)-1]
default:
return nil, ParseError{tok.pos, fmt.Sprintf("unexpected %s", tok.val)}
}
if rel.typ == tGREATER {
minVal = val
minInclusive = (eq.typ == tEQUAL)
} else { // if rel.typ == tLESS
maxVal = val
maxInclusive = (eq.typ == tEQUAL)
}
rp := newRangeParams(minVal, maxVal, minInclusive, maxInclusive, p.Loc)
q, err := rp.generate()
if err != nil {
return nil, ParseError{rel.pos, err.Error()}
}
if ctx.field != "" {
if fieldable, ok := q.(query.FieldableQuery); ok {
fieldable.SetField(ctx.field)
} else {
return nil, ParseError{ctx.fieldPos, "unexpected field"}
}
}
return q, nil
}