247 lines
5.0 KiB
Go
247 lines
5.0 KiB
Go
package jsonpath
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
opTypeIndex = iota
|
|
opTypeIndexRange
|
|
opTypeIndexWild
|
|
opTypeName
|
|
opTypeNameList
|
|
opTypeNameWild
|
|
)
|
|
|
|
type Path struct {
|
|
stringValue string
|
|
operators []*operator
|
|
captureEndValue bool
|
|
}
|
|
|
|
type operator struct {
|
|
typ int
|
|
indexStart int
|
|
indexEnd int
|
|
hasIndexEnd bool
|
|
keyStrings map[string]struct{}
|
|
|
|
whereClauseBytes []byte
|
|
dependentPaths []*Path
|
|
whereClause []Item
|
|
}
|
|
|
|
// nolint:gocognit
|
|
func genIndexKey(tr tokenReader) (*operator, error) {
|
|
k := &operator{}
|
|
|
|
var t *Item
|
|
|
|
var ok bool
|
|
|
|
if t, ok = tr.next(); !ok {
|
|
return nil, errors.New("expected number, key, or *, but got none")
|
|
}
|
|
|
|
switch t.typ {
|
|
case pathWildcard:
|
|
k.typ = opTypeIndexWild
|
|
k.indexStart = 0
|
|
|
|
if t, ok = tr.next(); !ok {
|
|
return nil, errors.New("expected ] after *, but got none")
|
|
}
|
|
|
|
if t.typ != pathBracketRight {
|
|
return nil, fmt.Errorf("expected ] after * instead of %q", t.val)
|
|
}
|
|
case pathIndex:
|
|
v, err := strconv.Atoi(string(t.val))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not parse %q into int64", t.val)
|
|
}
|
|
|
|
k.indexStart = v
|
|
k.indexEnd = v
|
|
k.hasIndexEnd = true
|
|
|
|
if t, ok = tr.next(); !ok {
|
|
return nil, errors.New("expected number or *, but got none")
|
|
}
|
|
|
|
switch t.typ {
|
|
case pathIndexRange:
|
|
if t, ok = tr.next(); !ok {
|
|
return nil, errors.New("expected number or *, but got none")
|
|
}
|
|
|
|
switch t.typ {
|
|
case pathIndex:
|
|
v, err := strconv.Atoi(string(t.val))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not parse %q into int64", t.val)
|
|
}
|
|
|
|
k.indexEnd = v - 1
|
|
k.hasIndexEnd = true
|
|
|
|
if t, ok = tr.next(); !ok || t.typ != pathBracketRight {
|
|
return nil, errors.New("expected ], but got none")
|
|
}
|
|
case pathBracketRight:
|
|
k.hasIndexEnd = false
|
|
default:
|
|
return nil, fmt.Errorf("unexpected value within brackets after index: %q", t.val)
|
|
}
|
|
|
|
k.typ = opTypeIndexRange
|
|
case pathBracketRight:
|
|
k.typ = opTypeIndex
|
|
default:
|
|
return nil, fmt.Errorf("unexpected value within brackets after index: %q", t.val)
|
|
}
|
|
case pathKey:
|
|
k.keyStrings = map[string]struct{}{
|
|
string(t.val[1 : len(t.val)-1]): {},
|
|
}
|
|
k.typ = opTypeName
|
|
|
|
if t, ok = tr.next(); !ok || t.typ != pathBracketRight {
|
|
return nil, errors.New("expected ], but got none")
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("unexpected value within brackets: %q", t.val)
|
|
}
|
|
|
|
return k, nil
|
|
}
|
|
|
|
func parsePath(pathString string) (*Path, error) {
|
|
lexer := NewSliceLexer([]byte(pathString), PATH)
|
|
|
|
p, err := tokensToOperators(lexer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.stringValue = pathString
|
|
|
|
//Generate dependent paths
|
|
for _, op := range p.operators {
|
|
if len(op.whereClauseBytes) > 0 {
|
|
var err error
|
|
|
|
trimmed := op.whereClauseBytes[1 : len(op.whereClauseBytes)-1]
|
|
whereLexer := NewSliceLexer(trimmed, EXPRESSION)
|
|
items := readerToArray(whereLexer)
|
|
|
|
if errItem, found := findErrors(items); found {
|
|
return nil, errors.New(string(errItem.val))
|
|
}
|
|
|
|
// transform expression into postfix form
|
|
op.whereClause, err = infixToPostFix(items[:len(items)-1]) // trim EOF
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
op.dependentPaths = make([]*Path, 0)
|
|
// parse all paths in expression
|
|
for _, item := range op.whereClause {
|
|
if item.typ == exprPath {
|
|
p, err := parsePath(string(item.val))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
op.dependentPaths = append(op.dependentPaths, p)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
func tokensToOperators(tr tokenReader) (*Path, error) {
|
|
q := &Path{
|
|
stringValue: "",
|
|
captureEndValue: false,
|
|
operators: make([]*operator, 0),
|
|
}
|
|
|
|
for {
|
|
p, ok := tr.next()
|
|
if !ok {
|
|
break
|
|
}
|
|
|
|
switch p.typ {
|
|
case pathRoot:
|
|
if len(q.operators) != 0 {
|
|
return nil, errors.New("unexpected root node after start")
|
|
}
|
|
|
|
continue
|
|
case pathCurrent:
|
|
if len(q.operators) != 0 {
|
|
return nil, errors.New("unexpected current node after start")
|
|
}
|
|
|
|
continue
|
|
case pathPeriod:
|
|
continue
|
|
case pathBracketLeft:
|
|
k, err := genIndexKey(tr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
q.operators = append(q.operators, k)
|
|
case pathKey:
|
|
keyName := p.val
|
|
|
|
if len(p.val) == 0 {
|
|
return nil, fmt.Errorf("key length is zero at %d", p.pos)
|
|
}
|
|
|
|
if p.val[0] == '"' && p.val[len(p.val)-1] == '"' {
|
|
keyName = p.val[1 : len(p.val)-1]
|
|
}
|
|
|
|
q.operators = append(
|
|
q.operators,
|
|
&operator{
|
|
typ: opTypeName,
|
|
keyStrings: map[string]struct{}{
|
|
string(keyName): {},
|
|
},
|
|
},
|
|
)
|
|
case pathWildcard:
|
|
q.operators = append(q.operators, &operator{typ: opTypeNameWild})
|
|
case pathValue:
|
|
q.captureEndValue = true
|
|
case pathWhere:
|
|
|
|
case pathExpression:
|
|
if len(q.operators) == 0 {
|
|
return nil, errors.New("cannot add where clause on last key")
|
|
}
|
|
|
|
last := q.operators[len(q.operators)-1]
|
|
if last.whereClauseBytes != nil {
|
|
return nil, errors.New("expression on last key already set")
|
|
}
|
|
|
|
last.whereClauseBytes = p.val
|
|
case pathError:
|
|
return q, errors.New(string(p.val))
|
|
}
|
|
}
|
|
|
|
return q, nil
|
|
}
|