mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-12 03:05:14 +00:00
DB service (#132)
This commit is contained in:
134
db/handler/parse.go
Normal file
134
db/handler/parse.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/crufter/lexer"
|
||||
)
|
||||
|
||||
var quoteEscape = fmt.Sprint(0x10FFFF)
|
||||
|
||||
const (
|
||||
itemIgnore = iota
|
||||
|
||||
// nouns
|
||||
itemAnd
|
||||
itemInt
|
||||
itemFieldName
|
||||
itemString
|
||||
itemBoolTrue
|
||||
itemBoolFalse
|
||||
|
||||
// ops
|
||||
itemEquals
|
||||
itemNotEquals
|
||||
itemLessThan
|
||||
itemGreaterThan
|
||||
itemLessThanEquals
|
||||
itemGreaterThanEquals
|
||||
)
|
||||
|
||||
var opToString = map[int]string{
|
||||
itemEquals: "==",
|
||||
itemNotEquals: "!=",
|
||||
itemLessThan: "<",
|
||||
itemGreaterThan: ">",
|
||||
itemLessThanEquals: "<=",
|
||||
itemGreaterThanEquals: ">=",
|
||||
}
|
||||
|
||||
var expressions = []lexer.TokenExpr{
|
||||
{`[ ]+`, itemIgnore}, // Whitespace
|
||||
{`==`, itemEquals},
|
||||
{`!=`, itemNotEquals},
|
||||
{`false`, itemBoolFalse},
|
||||
{`true`, itemBoolTrue},
|
||||
{`and`, itemAnd},
|
||||
{`<=`, itemLessThanEquals},
|
||||
{`>=`, itemGreaterThanEquals},
|
||||
{`<`, itemLessThan},
|
||||
{`>`, itemGreaterThan},
|
||||
{`[0-9]+`, itemInt},
|
||||
{`"(?:[^"\\]|\\.)*"`, itemString},
|
||||
{`[\<\>\!\=\+\-\|\&\*\/A-Za-z][A-Za-z0-9_]*`, itemFieldName},
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
Field string
|
||||
Op int
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func Parse(q string) ([]Query, error) {
|
||||
if strings.Contains(q, quoteEscape) {
|
||||
return nil, errors.New("query contains illegal max rune")
|
||||
}
|
||||
q = strings.Replace(q, `""`, quoteEscape, -1)
|
||||
tokens, err := lexer.Lex(q, expressions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries := []Query{}
|
||||
current := Query{}
|
||||
for i, token := range tokens {
|
||||
// and tokens should trigger a query
|
||||
// save and reset
|
||||
if token.Typ == itemAnd {
|
||||
queries = append(queries, current)
|
||||
current = Query{}
|
||||
continue
|
||||
}
|
||||
|
||||
// is an op
|
||||
if token.Typ >= itemEquals {
|
||||
current.Op = token.Typ
|
||||
continue
|
||||
}
|
||||
|
||||
// is a value
|
||||
switch token.Typ {
|
||||
case itemFieldName:
|
||||
current.Field = token.Text
|
||||
case itemString:
|
||||
switch current.Op {
|
||||
case itemEquals, itemNotEquals:
|
||||
default:
|
||||
return nil, fmt.Errorf("operator '%v' can't be used with strings", opToString[token.Typ])
|
||||
}
|
||||
|
||||
if len(token.Text) < 2 {
|
||||
return nil, fmt.Errorf("string literal too short: '%v'", token.Text)
|
||||
}
|
||||
current.Value = strings.Replace(token.Text[1:len(token.Text)-1], quoteEscape, `"`, -1)
|
||||
case itemBoolTrue:
|
||||
switch current.Op {
|
||||
case itemEquals, itemNotEquals:
|
||||
default:
|
||||
return nil, fmt.Errorf("operator '%v' can't be used with bools", opToString[token.Typ])
|
||||
}
|
||||
current.Value = true
|
||||
case itemBoolFalse:
|
||||
switch current.Op {
|
||||
case itemEquals, itemNotEquals:
|
||||
default:
|
||||
return nil, fmt.Errorf("operator '%v' can't be used with bools", opToString[token.Typ])
|
||||
}
|
||||
current.Value = false
|
||||
case itemInt:
|
||||
num, err := strconv.ParseInt(token.Text, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current.Value = num
|
||||
}
|
||||
|
||||
// if we are at last position, save last query
|
||||
if i == len(tokens)-1 {
|
||||
queries = append(queries, current)
|
||||
}
|
||||
}
|
||||
return queries, nil
|
||||
}
|
||||
Reference in New Issue
Block a user