Add route53 plugin (#1390)
* Update vendor Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Add route53 plugin This fix adds route53 plugin so that it is possible to query route53 record through CoreDNS. Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
parent
d699b89063
commit
584dd87c70
352 changed files with 81636 additions and 1798 deletions
4
vendor/github.com/jmespath/go-jmespath/.gitignore
generated
vendored
Normal file
4
vendor/github.com/jmespath/go-jmespath/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
jpgo
|
||||
jmespath-fuzz.zip
|
||||
cpu.out
|
||||
go-jmespath.test
|
9
vendor/github.com/jmespath/go-jmespath/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/jmespath/go-jmespath/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
language: go
|
||||
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
|
||||
install: go get -v -t ./...
|
||||
script: make test
|
13
vendor/github.com/jmespath/go-jmespath/LICENSE
generated
vendored
Normal file
13
vendor/github.com/jmespath/go-jmespath/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
Copyright 2015 James Saryerwinnie
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
44
vendor/github.com/jmespath/go-jmespath/Makefile
generated
vendored
Normal file
44
vendor/github.com/jmespath/go-jmespath/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
CMD = jpgo
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " test to run all the tests"
|
||||
@echo " build to build the library and jp executable"
|
||||
@echo " generate to run codegen"
|
||||
|
||||
|
||||
generate:
|
||||
go generate ./...
|
||||
|
||||
build:
|
||||
rm -f $(CMD)
|
||||
go build ./...
|
||||
rm -f cmd/$(CMD)/$(CMD) && cd cmd/$(CMD)/ && go build ./...
|
||||
mv cmd/$(CMD)/$(CMD) .
|
||||
|
||||
test:
|
||||
go test -v ./...
|
||||
|
||||
check:
|
||||
go vet ./...
|
||||
@echo "golint ./..."
|
||||
@lint=`golint ./...`; \
|
||||
lint=`echo "$$lint" | grep -v "astnodetype_string.go" | grep -v "toktype_string.go"`; \
|
||||
echo "$$lint"; \
|
||||
if [ "$$lint" != "" ]; then exit 1; fi
|
||||
|
||||
htmlc:
|
||||
go test -coverprofile="/tmp/jpcov" && go tool cover -html="/tmp/jpcov" && unlink /tmp/jpcov
|
||||
|
||||
buildfuzz:
|
||||
go-fuzz-build github.com/jmespath/go-jmespath/fuzz
|
||||
|
||||
fuzz: buildfuzz
|
||||
go-fuzz -bin=./jmespath-fuzz.zip -workdir=fuzz/testdata
|
||||
|
||||
bench:
|
||||
go test -bench . -cpuprofile cpu.out
|
||||
|
||||
pprof-cpu:
|
||||
go tool pprof ./go-jmespath.test ./cpu.out
|
7
vendor/github.com/jmespath/go-jmespath/README.md
generated
vendored
Normal file
7
vendor/github.com/jmespath/go-jmespath/README.md
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# go-jmespath - A JMESPath implementation in Go
|
||||
|
||||
[](https://travis-ci.org/jmespath/go-jmespath)
|
||||
|
||||
|
||||
|
||||
See http://jmespath.org for more info.
|
49
vendor/github.com/jmespath/go-jmespath/api.go
generated
vendored
Normal file
49
vendor/github.com/jmespath/go-jmespath/api.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package jmespath
|
||||
|
||||
import "strconv"
|
||||
|
||||
// JmesPath is the epresentation of a compiled JMES path query. A JmesPath is
|
||||
// safe for concurrent use by multiple goroutines.
|
||||
type JMESPath struct {
|
||||
ast ASTNode
|
||||
intr *treeInterpreter
|
||||
}
|
||||
|
||||
// Compile parses a JMESPath expression and returns, if successful, a JMESPath
|
||||
// object that can be used to match against data.
|
||||
func Compile(expression string) (*JMESPath, error) {
|
||||
parser := NewParser()
|
||||
ast, err := parser.Parse(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jmespath := &JMESPath{ast: ast, intr: newInterpreter()}
|
||||
return jmespath, nil
|
||||
}
|
||||
|
||||
// MustCompile is like Compile but panics if the expression cannot be parsed.
|
||||
// It simplifies safe initialization of global variables holding compiled
|
||||
// JMESPaths.
|
||||
func MustCompile(expression string) *JMESPath {
|
||||
jmespath, err := Compile(expression)
|
||||
if err != nil {
|
||||
panic(`jmespath: Compile(` + strconv.Quote(expression) + `): ` + err.Error())
|
||||
}
|
||||
return jmespath
|
||||
}
|
||||
|
||||
// Search evaluates a JMESPath expression against input data and returns the result.
|
||||
func (jp *JMESPath) Search(data interface{}) (interface{}, error) {
|
||||
return jp.intr.Execute(jp.ast, data)
|
||||
}
|
||||
|
||||
// Search evaluates a JMESPath expression against input data and returns the result.
|
||||
func Search(expression string, data interface{}) (interface{}, error) {
|
||||
intr := newInterpreter()
|
||||
parser := NewParser()
|
||||
ast, err := parser.Parse(expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return intr.Execute(ast, data)
|
||||
}
|
32
vendor/github.com/jmespath/go-jmespath/api_test.go
generated
vendored
Normal file
32
vendor/github.com/jmespath/go-jmespath/api_test.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidPrecompiledExpressionSearches(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := make(map[string]interface{})
|
||||
data["foo"] = "bar"
|
||||
precompiled, err := Compile("foo")
|
||||
assert.Nil(err)
|
||||
result, err := precompiled.Search(data)
|
||||
assert.Nil(err)
|
||||
assert.Equal("bar", result)
|
||||
}
|
||||
|
||||
func TestInvalidPrecompileErrors(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
_, err := Compile("not a valid expression")
|
||||
assert.NotNil(err)
|
||||
}
|
||||
|
||||
func TestInvalidMustCompilePanics(t *testing.T) {
|
||||
defer func() {
|
||||
r := recover()
|
||||
assert.NotNil(t, r)
|
||||
}()
|
||||
MustCompile("not a valid expression")
|
||||
}
|
16
vendor/github.com/jmespath/go-jmespath/astnodetype_string.go
generated
vendored
Normal file
16
vendor/github.com/jmespath/go-jmespath/astnodetype_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// generated by stringer -type astNodeType; DO NOT EDIT
|
||||
|
||||
package jmespath
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _astNodeType_name = "ASTEmptyASTComparatorASTCurrentNodeASTExpRefASTFunctionExpressionASTFieldASTFilterProjectionASTFlattenASTIdentityASTIndexASTIndexExpressionASTKeyValPairASTLiteralASTMultiSelectHashASTMultiSelectListASTOrExpressionASTAndExpressionASTNotExpressionASTPipeASTProjectionASTSubexpressionASTSliceASTValueProjection"
|
||||
|
||||
var _astNodeType_index = [...]uint16{0, 8, 21, 35, 44, 65, 73, 92, 102, 113, 121, 139, 152, 162, 180, 198, 213, 229, 245, 252, 265, 281, 289, 307}
|
||||
|
||||
func (i astNodeType) String() string {
|
||||
if i < 0 || i >= astNodeType(len(_astNodeType_index)-1) {
|
||||
return fmt.Sprintf("astNodeType(%d)", i)
|
||||
}
|
||||
return _astNodeType_name[_astNodeType_index[i]:_astNodeType_index[i+1]]
|
||||
}
|
123
vendor/github.com/jmespath/go-jmespath/compliance_test.go
generated
vendored
Normal file
123
vendor/github.com/jmespath/go-jmespath/compliance_test.go
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestSuite struct {
|
||||
Given interface{}
|
||||
TestCases []TestCase `json:"cases"`
|
||||
Comment string
|
||||
}
|
||||
type TestCase struct {
|
||||
Comment string
|
||||
Expression string
|
||||
Result interface{}
|
||||
Error string
|
||||
}
|
||||
|
||||
var whiteListed = []string{
|
||||
"compliance/basic.json",
|
||||
"compliance/current.json",
|
||||
"compliance/escape.json",
|
||||
"compliance/filters.json",
|
||||
"compliance/functions.json",
|
||||
"compliance/identifiers.json",
|
||||
"compliance/indices.json",
|
||||
"compliance/literal.json",
|
||||
"compliance/multiselect.json",
|
||||
"compliance/ormatch.json",
|
||||
"compliance/pipe.json",
|
||||
"compliance/slice.json",
|
||||
"compliance/syntax.json",
|
||||
"compliance/unicode.json",
|
||||
"compliance/wildcard.json",
|
||||
"compliance/boolean.json",
|
||||
}
|
||||
|
||||
func allowed(path string) bool {
|
||||
for _, el := range whiteListed {
|
||||
if el == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestCompliance(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var complianceFiles []string
|
||||
err := filepath.Walk("compliance", func(path string, _ os.FileInfo, _ error) error {
|
||||
//if strings.HasSuffix(path, ".json") {
|
||||
if allowed(path) {
|
||||
complianceFiles = append(complianceFiles, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if assert.Nil(err) {
|
||||
for _, filename := range complianceFiles {
|
||||
runComplianceTest(assert, filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runComplianceTest(assert *assert.Assertions, filename string) {
|
||||
var testSuites []TestSuite
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if assert.Nil(err) {
|
||||
err := json.Unmarshal(data, &testSuites)
|
||||
if assert.Nil(err) {
|
||||
for _, testsuite := range testSuites {
|
||||
runTestSuite(assert, testsuite, filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runTestSuite(assert *assert.Assertions, testsuite TestSuite, filename string) {
|
||||
for _, testcase := range testsuite.TestCases {
|
||||
if testcase.Error != "" {
|
||||
// This is a test case that verifies we error out properly.
|
||||
runSyntaxTestCase(assert, testsuite.Given, testcase, filename)
|
||||
} else {
|
||||
runTestCase(assert, testsuite.Given, testcase, filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runSyntaxTestCase(assert *assert.Assertions, given interface{}, testcase TestCase, filename string) {
|
||||
// Anything with an .Error means that we expect that JMESPath should return
|
||||
// an error when we try to evaluate the expression.
|
||||
_, err := Search(testcase.Expression, given)
|
||||
assert.NotNil(err, fmt.Sprintf("Expression: %s", testcase.Expression))
|
||||
}
|
||||
|
||||
func runTestCase(assert *assert.Assertions, given interface{}, testcase TestCase, filename string) {
|
||||
lexer := NewLexer()
|
||||
var err error
|
||||
_, err = lexer.tokenize(testcase.Expression)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("(%s) Could not lex expression: %s -- %s", filename, testcase.Expression, err.Error())
|
||||
assert.Fail(errMsg)
|
||||
return
|
||||
}
|
||||
parser := NewParser()
|
||||
_, err = parser.Parse(testcase.Expression)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("(%s) Could not parse expression: %s -- %s", filename, testcase.Expression, err.Error())
|
||||
assert.Fail(errMsg)
|
||||
return
|
||||
}
|
||||
actual, err := Search(testcase.Expression, given)
|
||||
if assert.Nil(err, fmt.Sprintf("Expression: %s", testcase.Expression)) {
|
||||
assert.Equal(testcase.Result, actual, fmt.Sprintf("Expression: %s", testcase.Expression))
|
||||
}
|
||||
}
|
842
vendor/github.com/jmespath/go-jmespath/functions.go
generated
vendored
Normal file
842
vendor/github.com/jmespath/go-jmespath/functions.go
generated
vendored
Normal file
|
@ -0,0 +1,842 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type jpFunction func(arguments []interface{}) (interface{}, error)
|
||||
|
||||
type jpType string
|
||||
|
||||
const (
|
||||
jpUnknown jpType = "unknown"
|
||||
jpNumber jpType = "number"
|
||||
jpString jpType = "string"
|
||||
jpArray jpType = "array"
|
||||
jpObject jpType = "object"
|
||||
jpArrayNumber jpType = "array[number]"
|
||||
jpArrayString jpType = "array[string]"
|
||||
jpExpref jpType = "expref"
|
||||
jpAny jpType = "any"
|
||||
)
|
||||
|
||||
type functionEntry struct {
|
||||
name string
|
||||
arguments []argSpec
|
||||
handler jpFunction
|
||||
hasExpRef bool
|
||||
}
|
||||
|
||||
type argSpec struct {
|
||||
types []jpType
|
||||
variadic bool
|
||||
}
|
||||
|
||||
type byExprString struct {
|
||||
intr *treeInterpreter
|
||||
node ASTNode
|
||||
items []interface{}
|
||||
hasError bool
|
||||
}
|
||||
|
||||
func (a *byExprString) Len() int {
|
||||
return len(a.items)
|
||||
}
|
||||
func (a *byExprString) Swap(i, j int) {
|
||||
a.items[i], a.items[j] = a.items[j], a.items[i]
|
||||
}
|
||||
func (a *byExprString) Less(i, j int) bool {
|
||||
first, err := a.intr.Execute(a.node, a.items[i])
|
||||
if err != nil {
|
||||
a.hasError = true
|
||||
// Return a dummy value.
|
||||
return true
|
||||
}
|
||||
ith, ok := first.(string)
|
||||
if !ok {
|
||||
a.hasError = true
|
||||
return true
|
||||
}
|
||||
second, err := a.intr.Execute(a.node, a.items[j])
|
||||
if err != nil {
|
||||
a.hasError = true
|
||||
// Return a dummy value.
|
||||
return true
|
||||
}
|
||||
jth, ok := second.(string)
|
||||
if !ok {
|
||||
a.hasError = true
|
||||
return true
|
||||
}
|
||||
return ith < jth
|
||||
}
|
||||
|
||||
type byExprFloat struct {
|
||||
intr *treeInterpreter
|
||||
node ASTNode
|
||||
items []interface{}
|
||||
hasError bool
|
||||
}
|
||||
|
||||
func (a *byExprFloat) Len() int {
|
||||
return len(a.items)
|
||||
}
|
||||
func (a *byExprFloat) Swap(i, j int) {
|
||||
a.items[i], a.items[j] = a.items[j], a.items[i]
|
||||
}
|
||||
func (a *byExprFloat) Less(i, j int) bool {
|
||||
first, err := a.intr.Execute(a.node, a.items[i])
|
||||
if err != nil {
|
||||
a.hasError = true
|
||||
// Return a dummy value.
|
||||
return true
|
||||
}
|
||||
ith, ok := first.(float64)
|
||||
if !ok {
|
||||
a.hasError = true
|
||||
return true
|
||||
}
|
||||
second, err := a.intr.Execute(a.node, a.items[j])
|
||||
if err != nil {
|
||||
a.hasError = true
|
||||
// Return a dummy value.
|
||||
return true
|
||||
}
|
||||
jth, ok := second.(float64)
|
||||
if !ok {
|
||||
a.hasError = true
|
||||
return true
|
||||
}
|
||||
return ith < jth
|
||||
}
|
||||
|
||||
type functionCaller struct {
|
||||
functionTable map[string]functionEntry
|
||||
}
|
||||
|
||||
func newFunctionCaller() *functionCaller {
|
||||
caller := &functionCaller{}
|
||||
caller.functionTable = map[string]functionEntry{
|
||||
"length": {
|
||||
name: "length",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpString, jpArray, jpObject}},
|
||||
},
|
||||
handler: jpfLength,
|
||||
},
|
||||
"starts_with": {
|
||||
name: "starts_with",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpString}},
|
||||
{types: []jpType{jpString}},
|
||||
},
|
||||
handler: jpfStartsWith,
|
||||
},
|
||||
"abs": {
|
||||
name: "abs",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpNumber}},
|
||||
},
|
||||
handler: jpfAbs,
|
||||
},
|
||||
"avg": {
|
||||
name: "avg",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayNumber}},
|
||||
},
|
||||
handler: jpfAvg,
|
||||
},
|
||||
"ceil": {
|
||||
name: "ceil",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpNumber}},
|
||||
},
|
||||
handler: jpfCeil,
|
||||
},
|
||||
"contains": {
|
||||
name: "contains",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray, jpString}},
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfContains,
|
||||
},
|
||||
"ends_with": {
|
||||
name: "ends_with",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpString}},
|
||||
{types: []jpType{jpString}},
|
||||
},
|
||||
handler: jpfEndsWith,
|
||||
},
|
||||
"floor": {
|
||||
name: "floor",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpNumber}},
|
||||
},
|
||||
handler: jpfFloor,
|
||||
},
|
||||
"map": {
|
||||
name: "amp",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpExpref}},
|
||||
{types: []jpType{jpArray}},
|
||||
},
|
||||
handler: jpfMap,
|
||||
hasExpRef: true,
|
||||
},
|
||||
"max": {
|
||||
name: "max",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayNumber, jpArrayString}},
|
||||
},
|
||||
handler: jpfMax,
|
||||
},
|
||||
"merge": {
|
||||
name: "merge",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpObject}, variadic: true},
|
||||
},
|
||||
handler: jpfMerge,
|
||||
},
|
||||
"max_by": {
|
||||
name: "max_by",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray}},
|
||||
{types: []jpType{jpExpref}},
|
||||
},
|
||||
handler: jpfMaxBy,
|
||||
hasExpRef: true,
|
||||
},
|
||||
"sum": {
|
||||
name: "sum",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayNumber}},
|
||||
},
|
||||
handler: jpfSum,
|
||||
},
|
||||
"min": {
|
||||
name: "min",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayNumber, jpArrayString}},
|
||||
},
|
||||
handler: jpfMin,
|
||||
},
|
||||
"min_by": {
|
||||
name: "min_by",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray}},
|
||||
{types: []jpType{jpExpref}},
|
||||
},
|
||||
handler: jpfMinBy,
|
||||
hasExpRef: true,
|
||||
},
|
||||
"type": {
|
||||
name: "type",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfType,
|
||||
},
|
||||
"keys": {
|
||||
name: "keys",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpObject}},
|
||||
},
|
||||
handler: jpfKeys,
|
||||
},
|
||||
"values": {
|
||||
name: "values",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpObject}},
|
||||
},
|
||||
handler: jpfValues,
|
||||
},
|
||||
"sort": {
|
||||
name: "sort",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArrayString, jpArrayNumber}},
|
||||
},
|
||||
handler: jpfSort,
|
||||
},
|
||||
"sort_by": {
|
||||
name: "sort_by",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray}},
|
||||
{types: []jpType{jpExpref}},
|
||||
},
|
||||
handler: jpfSortBy,
|
||||
hasExpRef: true,
|
||||
},
|
||||
"join": {
|
||||
name: "join",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpString}},
|
||||
{types: []jpType{jpArrayString}},
|
||||
},
|
||||
handler: jpfJoin,
|
||||
},
|
||||
"reverse": {
|
||||
name: "reverse",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpArray, jpString}},
|
||||
},
|
||||
handler: jpfReverse,
|
||||
},
|
||||
"to_array": {
|
||||
name: "to_array",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfToArray,
|
||||
},
|
||||
"to_string": {
|
||||
name: "to_string",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfToString,
|
||||
},
|
||||
"to_number": {
|
||||
name: "to_number",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}},
|
||||
},
|
||||
handler: jpfToNumber,
|
||||
},
|
||||
"not_null": {
|
||||
name: "not_null",
|
||||
arguments: []argSpec{
|
||||
{types: []jpType{jpAny}, variadic: true},
|
||||
},
|
||||
handler: jpfNotNull,
|
||||
},
|
||||
}
|
||||
return caller
|
||||
}
|
||||
|
||||
func (e *functionEntry) resolveArgs(arguments []interface{}) ([]interface{}, error) {
|
||||
if len(e.arguments) == 0 {
|
||||
return arguments, nil
|
||||
}
|
||||
if !e.arguments[len(e.arguments)-1].variadic {
|
||||
if len(e.arguments) != len(arguments) {
|
||||
return nil, errors.New("incorrect number of args")
|
||||
}
|
||||
for i, spec := range e.arguments {
|
||||
userArg := arguments[i]
|
||||
err := spec.typeCheck(userArg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return arguments, nil
|
||||
}
|
||||
if len(arguments) < len(e.arguments) {
|
||||
return nil, errors.New("Invalid arity.")
|
||||
}
|
||||
return arguments, nil
|
||||
}
|
||||
|
||||
func (a *argSpec) typeCheck(arg interface{}) error {
|
||||
for _, t := range a.types {
|
||||
switch t {
|
||||
case jpNumber:
|
||||
if _, ok := arg.(float64); ok {
|
||||
return nil
|
||||
}
|
||||
case jpString:
|
||||
if _, ok := arg.(string); ok {
|
||||
return nil
|
||||
}
|
||||
case jpArray:
|
||||
if isSliceType(arg) {
|
||||
return nil
|
||||
}
|
||||
case jpObject:
|
||||
if _, ok := arg.(map[string]interface{}); ok {
|
||||
return nil
|
||||
}
|
||||
case jpArrayNumber:
|
||||
if _, ok := toArrayNum(arg); ok {
|
||||
return nil
|
||||
}
|
||||
case jpArrayString:
|
||||
if _, ok := toArrayStr(arg); ok {
|
||||
return nil
|
||||
}
|
||||
case jpAny:
|
||||
return nil
|
||||
case jpExpref:
|
||||
if _, ok := arg.(expRef); ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("Invalid type for: %v, expected: %#v", arg, a.types)
|
||||
}
|
||||
|
||||
func (f *functionCaller) CallFunction(name string, arguments []interface{}, intr *treeInterpreter) (interface{}, error) {
|
||||
entry, ok := f.functionTable[name]
|
||||
if !ok {
|
||||
return nil, errors.New("unknown function: " + name)
|
||||
}
|
||||
resolvedArgs, err := entry.resolveArgs(arguments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry.hasExpRef {
|
||||
var extra []interface{}
|
||||
extra = append(extra, intr)
|
||||
resolvedArgs = append(extra, resolvedArgs...)
|
||||
}
|
||||
return entry.handler(resolvedArgs)
|
||||
}
|
||||
|
||||
func jpfAbs(arguments []interface{}) (interface{}, error) {
|
||||
num := arguments[0].(float64)
|
||||
return math.Abs(num), nil
|
||||
}
|
||||
|
||||
func jpfLength(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0]
|
||||
if c, ok := arg.(string); ok {
|
||||
return float64(utf8.RuneCountInString(c)), nil
|
||||
} else if isSliceType(arg) {
|
||||
v := reflect.ValueOf(arg)
|
||||
return float64(v.Len()), nil
|
||||
} else if c, ok := arg.(map[string]interface{}); ok {
|
||||
return float64(len(c)), nil
|
||||
}
|
||||
return nil, errors.New("could not compute length()")
|
||||
}
|
||||
|
||||
func jpfStartsWith(arguments []interface{}) (interface{}, error) {
|
||||
search := arguments[0].(string)
|
||||
prefix := arguments[1].(string)
|
||||
return strings.HasPrefix(search, prefix), nil
|
||||
}
|
||||
|
||||
func jpfAvg(arguments []interface{}) (interface{}, error) {
|
||||
// We've already type checked the value so we can safely use
|
||||
// type assertions.
|
||||
args := arguments[0].([]interface{})
|
||||
length := float64(len(args))
|
||||
numerator := 0.0
|
||||
for _, n := range args {
|
||||
numerator += n.(float64)
|
||||
}
|
||||
return numerator / length, nil
|
||||
}
|
||||
func jpfCeil(arguments []interface{}) (interface{}, error) {
|
||||
val := arguments[0].(float64)
|
||||
return math.Ceil(val), nil
|
||||
}
|
||||
func jpfContains(arguments []interface{}) (interface{}, error) {
|
||||
search := arguments[0]
|
||||
el := arguments[1]
|
||||
if searchStr, ok := search.(string); ok {
|
||||
if elStr, ok := el.(string); ok {
|
||||
return strings.Index(searchStr, elStr) != -1, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
// Otherwise this is a generic contains for []interface{}
|
||||
general := search.([]interface{})
|
||||
for _, item := range general {
|
||||
if item == el {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func jpfEndsWith(arguments []interface{}) (interface{}, error) {
|
||||
search := arguments[0].(string)
|
||||
suffix := arguments[1].(string)
|
||||
return strings.HasSuffix(search, suffix), nil
|
||||
}
|
||||
func jpfFloor(arguments []interface{}) (interface{}, error) {
|
||||
val := arguments[0].(float64)
|
||||
return math.Floor(val), nil
|
||||
}
|
||||
func jpfMap(arguments []interface{}) (interface{}, error) {
|
||||
intr := arguments[0].(*treeInterpreter)
|
||||
exp := arguments[1].(expRef)
|
||||
node := exp.ref
|
||||
arr := arguments[2].([]interface{})
|
||||
mapped := make([]interface{}, 0, len(arr))
|
||||
for _, value := range arr {
|
||||
current, err := intr.Execute(node, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapped = append(mapped, current)
|
||||
}
|
||||
return mapped, nil
|
||||
}
|
||||
func jpfMax(arguments []interface{}) (interface{}, error) {
|
||||
if items, ok := toArrayNum(arguments[0]); ok {
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(items) == 1 {
|
||||
return items[0], nil
|
||||
}
|
||||
best := items[0]
|
||||
for _, item := range items[1:] {
|
||||
if item > best {
|
||||
best = item
|
||||
}
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
// Otherwise we're dealing with a max() of strings.
|
||||
items, _ := toArrayStr(arguments[0])
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(items) == 1 {
|
||||
return items[0], nil
|
||||
}
|
||||
best := items[0]
|
||||
for _, item := range items[1:] {
|
||||
if item > best {
|
||||
best = item
|
||||
}
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
func jpfMerge(arguments []interface{}) (interface{}, error) {
|
||||
final := make(map[string]interface{})
|
||||
for _, m := range arguments {
|
||||
mapped := m.(map[string]interface{})
|
||||
for key, value := range mapped {
|
||||
final[key] = value
|
||||
}
|
||||
}
|
||||
return final, nil
|
||||
}
|
||||
func jpfMaxBy(arguments []interface{}) (interface{}, error) {
|
||||
intr := arguments[0].(*treeInterpreter)
|
||||
arr := arguments[1].([]interface{})
|
||||
exp := arguments[2].(expRef)
|
||||
node := exp.ref
|
||||
if len(arr) == 0 {
|
||||
return nil, nil
|
||||
} else if len(arr) == 1 {
|
||||
return arr[0], nil
|
||||
}
|
||||
start, err := intr.Execute(node, arr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t := start.(type) {
|
||||
case float64:
|
||||
bestVal := t
|
||||
bestItem := arr[0]
|
||||
for _, item := range arr[1:] {
|
||||
result, err := intr.Execute(node, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, ok := result.(float64)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type, must be number")
|
||||
}
|
||||
if current > bestVal {
|
||||
bestVal = current
|
||||
bestItem = item
|
||||
}
|
||||
}
|
||||
return bestItem, nil
|
||||
case string:
|
||||
bestVal := t
|
||||
bestItem := arr[0]
|
||||
for _, item := range arr[1:] {
|
||||
result, err := intr.Execute(node, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, ok := result.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type, must be string")
|
||||
}
|
||||
if current > bestVal {
|
||||
bestVal = current
|
||||
bestItem = item
|
||||
}
|
||||
}
|
||||
return bestItem, nil
|
||||
default:
|
||||
return nil, errors.New("invalid type, must be number of string")
|
||||
}
|
||||
}
|
||||
func jpfSum(arguments []interface{}) (interface{}, error) {
|
||||
items, _ := toArrayNum(arguments[0])
|
||||
sum := 0.0
|
||||
for _, item := range items {
|
||||
sum += item
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func jpfMin(arguments []interface{}) (interface{}, error) {
|
||||
if items, ok := toArrayNum(arguments[0]); ok {
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(items) == 1 {
|
||||
return items[0], nil
|
||||
}
|
||||
best := items[0]
|
||||
for _, item := range items[1:] {
|
||||
if item < best {
|
||||
best = item
|
||||
}
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
items, _ := toArrayStr(arguments[0])
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(items) == 1 {
|
||||
return items[0], nil
|
||||
}
|
||||
best := items[0]
|
||||
for _, item := range items[1:] {
|
||||
if item < best {
|
||||
best = item
|
||||
}
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
|
||||
func jpfMinBy(arguments []interface{}) (interface{}, error) {
|
||||
intr := arguments[0].(*treeInterpreter)
|
||||
arr := arguments[1].([]interface{})
|
||||
exp := arguments[2].(expRef)
|
||||
node := exp.ref
|
||||
if len(arr) == 0 {
|
||||
return nil, nil
|
||||
} else if len(arr) == 1 {
|
||||
return arr[0], nil
|
||||
}
|
||||
start, err := intr.Execute(node, arr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t, ok := start.(float64); ok {
|
||||
bestVal := t
|
||||
bestItem := arr[0]
|
||||
for _, item := range arr[1:] {
|
||||
result, err := intr.Execute(node, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, ok := result.(float64)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type, must be number")
|
||||
}
|
||||
if current < bestVal {
|
||||
bestVal = current
|
||||
bestItem = item
|
||||
}
|
||||
}
|
||||
return bestItem, nil
|
||||
} else if t, ok := start.(string); ok {
|
||||
bestVal := t
|
||||
bestItem := arr[0]
|
||||
for _, item := range arr[1:] {
|
||||
result, err := intr.Execute(node, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current, ok := result.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid type, must be string")
|
||||
}
|
||||
if current < bestVal {
|
||||
bestVal = current
|
||||
bestItem = item
|
||||
}
|
||||
}
|
||||
return bestItem, nil
|
||||
} else {
|
||||
return nil, errors.New("invalid type, must be number of string")
|
||||
}
|
||||
}
|
||||
func jpfType(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0]
|
||||
if _, ok := arg.(float64); ok {
|
||||
return "number", nil
|
||||
}
|
||||
if _, ok := arg.(string); ok {
|
||||
return "string", nil
|
||||
}
|
||||
if _, ok := arg.([]interface{}); ok {
|
||||
return "array", nil
|
||||
}
|
||||
if _, ok := arg.(map[string]interface{}); ok {
|
||||
return "object", nil
|
||||
}
|
||||
if arg == nil {
|
||||
return "null", nil
|
||||
}
|
||||
if arg == true || arg == false {
|
||||
return "boolean", nil
|
||||
}
|
||||
return nil, errors.New("unknown type")
|
||||
}
|
||||
func jpfKeys(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0].(map[string]interface{})
|
||||
collected := make([]interface{}, 0, len(arg))
|
||||
for key := range arg {
|
||||
collected = append(collected, key)
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
func jpfValues(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0].(map[string]interface{})
|
||||
collected := make([]interface{}, 0, len(arg))
|
||||
for _, value := range arg {
|
||||
collected = append(collected, value)
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
func jpfSort(arguments []interface{}) (interface{}, error) {
|
||||
if items, ok := toArrayNum(arguments[0]); ok {
|
||||
d := sort.Float64Slice(items)
|
||||
sort.Stable(d)
|
||||
final := make([]interface{}, len(d))
|
||||
for i, val := range d {
|
||||
final[i] = val
|
||||
}
|
||||
return final, nil
|
||||
}
|
||||
// Otherwise we're dealing with sort()'ing strings.
|
||||
items, _ := toArrayStr(arguments[0])
|
||||
d := sort.StringSlice(items)
|
||||
sort.Stable(d)
|
||||
final := make([]interface{}, len(d))
|
||||
for i, val := range d {
|
||||
final[i] = val
|
||||
}
|
||||
return final, nil
|
||||
}
|
||||
func jpfSortBy(arguments []interface{}) (interface{}, error) {
|
||||
intr := arguments[0].(*treeInterpreter)
|
||||
arr := arguments[1].([]interface{})
|
||||
exp := arguments[2].(expRef)
|
||||
node := exp.ref
|
||||
if len(arr) == 0 {
|
||||
return arr, nil
|
||||
} else if len(arr) == 1 {
|
||||
return arr, nil
|
||||
}
|
||||
start, err := intr.Execute(node, arr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := start.(float64); ok {
|
||||
sortable := &byExprFloat{intr, node, arr, false}
|
||||
sort.Stable(sortable)
|
||||
if sortable.hasError {
|
||||
return nil, errors.New("error in sort_by comparison")
|
||||
}
|
||||
return arr, nil
|
||||
} else if _, ok := start.(string); ok {
|
||||
sortable := &byExprString{intr, node, arr, false}
|
||||
sort.Stable(sortable)
|
||||
if sortable.hasError {
|
||||
return nil, errors.New("error in sort_by comparison")
|
||||
}
|
||||
return arr, nil
|
||||
} else {
|
||||
return nil, errors.New("invalid type, must be number of string")
|
||||
}
|
||||
}
|
||||
func jpfJoin(arguments []interface{}) (interface{}, error) {
|
||||
sep := arguments[0].(string)
|
||||
// We can't just do arguments[1].([]string), we have to
|
||||
// manually convert each item to a string.
|
||||
arrayStr := []string{}
|
||||
for _, item := range arguments[1].([]interface{}) {
|
||||
arrayStr = append(arrayStr, item.(string))
|
||||
}
|
||||
return strings.Join(arrayStr, sep), nil
|
||||
}
|
||||
func jpfReverse(arguments []interface{}) (interface{}, error) {
|
||||
if s, ok := arguments[0].(string); ok {
|
||||
r := []rune(s)
|
||||
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
return string(r), nil
|
||||
}
|
||||
items := arguments[0].([]interface{})
|
||||
length := len(items)
|
||||
reversed := make([]interface{}, length)
|
||||
for i, item := range items {
|
||||
reversed[length-(i+1)] = item
|
||||
}
|
||||
return reversed, nil
|
||||
}
|
||||
func jpfToArray(arguments []interface{}) (interface{}, error) {
|
||||
if _, ok := arguments[0].([]interface{}); ok {
|
||||
return arguments[0], nil
|
||||
}
|
||||
return arguments[:1:1], nil
|
||||
}
|
||||
func jpfToString(arguments []interface{}) (interface{}, error) {
|
||||
if v, ok := arguments[0].(string); ok {
|
||||
return v, nil
|
||||
}
|
||||
result, err := json.Marshal(arguments[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(result), nil
|
||||
}
|
||||
func jpfToNumber(arguments []interface{}) (interface{}, error) {
|
||||
arg := arguments[0]
|
||||
if v, ok := arg.(float64); ok {
|
||||
return v, nil
|
||||
}
|
||||
if v, ok := arg.(string); ok {
|
||||
conv, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return conv, nil
|
||||
}
|
||||
if _, ok := arg.([]interface{}); ok {
|
||||
return nil, nil
|
||||
}
|
||||
if _, ok := arg.(map[string]interface{}); ok {
|
||||
return nil, nil
|
||||
}
|
||||
if arg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if arg == true || arg == false {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("unknown type")
|
||||
}
|
||||
func jpfNotNull(arguments []interface{}) (interface{}, error) {
|
||||
for _, arg := range arguments {
|
||||
if arg != nil {
|
||||
return arg, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
418
vendor/github.com/jmespath/go-jmespath/interpreter.go
generated
vendored
Normal file
418
vendor/github.com/jmespath/go-jmespath/interpreter.go
generated
vendored
Normal file
|
@ -0,0 +1,418 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
/* This is a tree based interpreter. It walks the AST and directly
|
||||
interprets the AST to search through a JSON document.
|
||||
*/
|
||||
|
||||
type treeInterpreter struct {
|
||||
fCall *functionCaller
|
||||
}
|
||||
|
||||
func newInterpreter() *treeInterpreter {
|
||||
interpreter := treeInterpreter{}
|
||||
interpreter.fCall = newFunctionCaller()
|
||||
return &interpreter
|
||||
}
|
||||
|
||||
type expRef struct {
|
||||
ref ASTNode
|
||||
}
|
||||
|
||||
// Execute takes an ASTNode and input data and interprets the AST directly.
|
||||
// It will produce the result of applying the JMESPath expression associated
|
||||
// with the ASTNode to the input data "value".
|
||||
func (intr *treeInterpreter) Execute(node ASTNode, value interface{}) (interface{}, error) {
|
||||
switch node.nodeType {
|
||||
case ASTComparator:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
right, err := intr.Execute(node.children[1], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch node.value {
|
||||
case tEQ:
|
||||
return objsEqual(left, right), nil
|
||||
case tNE:
|
||||
return !objsEqual(left, right), nil
|
||||
}
|
||||
leftNum, ok := left.(float64)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
rightNum, ok := right.(float64)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
switch node.value {
|
||||
case tGT:
|
||||
return leftNum > rightNum, nil
|
||||
case tGTE:
|
||||
return leftNum >= rightNum, nil
|
||||
case tLT:
|
||||
return leftNum < rightNum, nil
|
||||
case tLTE:
|
||||
return leftNum <= rightNum, nil
|
||||
}
|
||||
case ASTExpRef:
|
||||
return expRef{ref: node.children[0]}, nil
|
||||
case ASTFunctionExpression:
|
||||
resolvedArgs := []interface{}{}
|
||||
for _, arg := range node.children {
|
||||
current, err := intr.Execute(arg, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolvedArgs = append(resolvedArgs, current)
|
||||
}
|
||||
return intr.fCall.CallFunction(node.value.(string), resolvedArgs, intr)
|
||||
case ASTField:
|
||||
if m, ok := value.(map[string]interface{}); ok {
|
||||
key := node.value.(string)
|
||||
return m[key], nil
|
||||
}
|
||||
return intr.fieldFromStruct(node.value.(string), value)
|
||||
case ASTFilterProjection:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
sliceType, ok := left.([]interface{})
|
||||
if !ok {
|
||||
if isSliceType(left) {
|
||||
return intr.filterProjectionWithReflection(node, left)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
compareNode := node.children[2]
|
||||
collected := []interface{}{}
|
||||
for _, element := range sliceType {
|
||||
result, err := intr.Execute(compareNode, element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isFalse(result) {
|
||||
current, err := intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current != nil {
|
||||
collected = append(collected, current)
|
||||
}
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
case ASTFlatten:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
sliceType, ok := left.([]interface{})
|
||||
if !ok {
|
||||
// If we can't type convert to []interface{}, there's
|
||||
// a chance this could still work via reflection if we're
|
||||
// dealing with user provided types.
|
||||
if isSliceType(left) {
|
||||
return intr.flattenWithReflection(left)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
flattened := []interface{}{}
|
||||
for _, element := range sliceType {
|
||||
if elementSlice, ok := element.([]interface{}); ok {
|
||||
flattened = append(flattened, elementSlice...)
|
||||
} else if isSliceType(element) {
|
||||
reflectFlat := []interface{}{}
|
||||
v := reflect.ValueOf(element)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
reflectFlat = append(reflectFlat, v.Index(i).Interface())
|
||||
}
|
||||
flattened = append(flattened, reflectFlat...)
|
||||
} else {
|
||||
flattened = append(flattened, element)
|
||||
}
|
||||
}
|
||||
return flattened, nil
|
||||
case ASTIdentity, ASTCurrentNode:
|
||||
return value, nil
|
||||
case ASTIndex:
|
||||
if sliceType, ok := value.([]interface{}); ok {
|
||||
index := node.value.(int)
|
||||
if index < 0 {
|
||||
index += len(sliceType)
|
||||
}
|
||||
if index < len(sliceType) && index >= 0 {
|
||||
return sliceType[index], nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
// Otherwise try via reflection.
|
||||
rv := reflect.ValueOf(value)
|
||||
if rv.Kind() == reflect.Slice {
|
||||
index := node.value.(int)
|
||||
if index < 0 {
|
||||
index += rv.Len()
|
||||
}
|
||||
if index < rv.Len() && index >= 0 {
|
||||
v := rv.Index(index)
|
||||
return v.Interface(), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
case ASTKeyValPair:
|
||||
return intr.Execute(node.children[0], value)
|
||||
case ASTLiteral:
|
||||
return node.value, nil
|
||||
case ASTMultiSelectHash:
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
collected := make(map[string]interface{})
|
||||
for _, child := range node.children {
|
||||
current, err := intr.Execute(child, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := child.value.(string)
|
||||
collected[key] = current
|
||||
}
|
||||
return collected, nil
|
||||
case ASTMultiSelectList:
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
collected := []interface{}{}
|
||||
for _, child := range node.children {
|
||||
current, err := intr.Execute(child, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
collected = append(collected, current)
|
||||
}
|
||||
return collected, nil
|
||||
case ASTOrExpression:
|
||||
matched, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isFalse(matched) {
|
||||
matched, err = intr.Execute(node.children[1], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return matched, nil
|
||||
case ASTAndExpression:
|
||||
matched, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isFalse(matched) {
|
||||
return matched, nil
|
||||
}
|
||||
return intr.Execute(node.children[1], value)
|
||||
case ASTNotExpression:
|
||||
matched, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isFalse(matched) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
case ASTPipe:
|
||||
result := value
|
||||
var err error
|
||||
for _, child := range node.children {
|
||||
result, err = intr.Execute(child, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
case ASTProjection:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sliceType, ok := left.([]interface{})
|
||||
if !ok {
|
||||
if isSliceType(left) {
|
||||
return intr.projectWithReflection(node, left)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
collected := []interface{}{}
|
||||
var current interface{}
|
||||
for _, element := range sliceType {
|
||||
current, err = intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current != nil {
|
||||
collected = append(collected, current)
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
case ASTSubexpression, ASTIndexExpression:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return intr.Execute(node.children[1], left)
|
||||
case ASTSlice:
|
||||
sliceType, ok := value.([]interface{})
|
||||
if !ok {
|
||||
if isSliceType(value) {
|
||||
return intr.sliceWithReflection(node, value)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
parts := node.value.([]*int)
|
||||
sliceParams := make([]sliceParam, 3)
|
||||
for i, part := range parts {
|
||||
if part != nil {
|
||||
sliceParams[i].Specified = true
|
||||
sliceParams[i].N = *part
|
||||
}
|
||||
}
|
||||
return slice(sliceType, sliceParams)
|
||||
case ASTValueProjection:
|
||||
left, err := intr.Execute(node.children[0], value)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
mapType, ok := left.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
values := make([]interface{}, len(mapType))
|
||||
for _, value := range mapType {
|
||||
values = append(values, value)
|
||||
}
|
||||
collected := []interface{}{}
|
||||
for _, element := range values {
|
||||
current, err := intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current != nil {
|
||||
collected = append(collected, current)
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
return nil, errors.New("Unknown AST node: " + node.nodeType.String())
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) fieldFromStruct(key string, value interface{}) (interface{}, error) {
|
||||
rv := reflect.ValueOf(value)
|
||||
first, n := utf8.DecodeRuneInString(key)
|
||||
fieldName := string(unicode.ToUpper(first)) + key[n:]
|
||||
if rv.Kind() == reflect.Struct {
|
||||
v := rv.FieldByName(fieldName)
|
||||
if !v.IsValid() {
|
||||
return nil, nil
|
||||
}
|
||||
return v.Interface(), nil
|
||||
} else if rv.Kind() == reflect.Ptr {
|
||||
// Handle multiple levels of indirection?
|
||||
if rv.IsNil() {
|
||||
return nil, nil
|
||||
}
|
||||
rv = rv.Elem()
|
||||
v := rv.FieldByName(fieldName)
|
||||
if !v.IsValid() {
|
||||
return nil, nil
|
||||
}
|
||||
return v.Interface(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) flattenWithReflection(value interface{}) (interface{}, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
flattened := []interface{}{}
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i).Interface()
|
||||
if reflect.TypeOf(element).Kind() == reflect.Slice {
|
||||
// Then insert the contents of the element
|
||||
// slice into the flattened slice,
|
||||
// i.e flattened = append(flattened, mySlice...)
|
||||
elementV := reflect.ValueOf(element)
|
||||
for j := 0; j < elementV.Len(); j++ {
|
||||
flattened = append(
|
||||
flattened, elementV.Index(j).Interface())
|
||||
}
|
||||
} else {
|
||||
flattened = append(flattened, element)
|
||||
}
|
||||
}
|
||||
return flattened, nil
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) sliceWithReflection(node ASTNode, value interface{}) (interface{}, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
parts := node.value.([]*int)
|
||||
sliceParams := make([]sliceParam, 3)
|
||||
for i, part := range parts {
|
||||
if part != nil {
|
||||
sliceParams[i].Specified = true
|
||||
sliceParams[i].N = *part
|
||||
}
|
||||
}
|
||||
final := []interface{}{}
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i).Interface()
|
||||
final = append(final, element)
|
||||
}
|
||||
return slice(final, sliceParams)
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) filterProjectionWithReflection(node ASTNode, value interface{}) (interface{}, error) {
|
||||
compareNode := node.children[2]
|
||||
collected := []interface{}{}
|
||||
v := reflect.ValueOf(value)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i).Interface()
|
||||
result, err := intr.Execute(compareNode, element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isFalse(result) {
|
||||
current, err := intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current != nil {
|
||||
collected = append(collected, current)
|
||||
}
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
}
|
||||
|
||||
func (intr *treeInterpreter) projectWithReflection(node ASTNode, value interface{}) (interface{}, error) {
|
||||
collected := []interface{}{}
|
||||
v := reflect.ValueOf(value)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
element := v.Index(i).Interface()
|
||||
result, err := intr.Execute(node.children[1], element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result != nil {
|
||||
collected = append(collected, result)
|
||||
}
|
||||
}
|
||||
return collected, nil
|
||||
}
|
221
vendor/github.com/jmespath/go-jmespath/interpreter_test.go
generated
vendored
Normal file
221
vendor/github.com/jmespath/go-jmespath/interpreter_test.go
generated
vendored
Normal file
|
@ -0,0 +1,221 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type scalars struct {
|
||||
Foo string
|
||||
Bar string
|
||||
}
|
||||
|
||||
type sliceType struct {
|
||||
A string
|
||||
B []scalars
|
||||
C []*scalars
|
||||
}
|
||||
|
||||
type benchmarkStruct struct {
|
||||
Fooasdfasdfasdfasdf string
|
||||
}
|
||||
|
||||
type benchmarkNested struct {
|
||||
Fooasdfasdfasdfasdf nestedA
|
||||
}
|
||||
|
||||
type nestedA struct {
|
||||
Fooasdfasdfasdfasdf nestedB
|
||||
}
|
||||
|
||||
type nestedB struct {
|
||||
Fooasdfasdfasdfasdf nestedC
|
||||
}
|
||||
|
||||
type nestedC struct {
|
||||
Fooasdfasdfasdfasdf string
|
||||
}
|
||||
|
||||
type nestedSlice struct {
|
||||
A []sliceType
|
||||
}
|
||||
|
||||
func TestCanSupportEmptyInterface(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := make(map[string]interface{})
|
||||
data["foo"] = "bar"
|
||||
result, err := Search("foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal("bar", result)
|
||||
}
|
||||
|
||||
func TestCanSupportUserDefinedStructsValue(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
s := scalars{Foo: "one", Bar: "bar"}
|
||||
result, err := Search("Foo", s)
|
||||
assert.Nil(err)
|
||||
assert.Equal("one", result)
|
||||
}
|
||||
|
||||
func TestCanSupportUserDefinedStructsRef(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
s := scalars{Foo: "one", Bar: "bar"}
|
||||
result, err := Search("Foo", &s)
|
||||
assert.Nil(err)
|
||||
assert.Equal("one", result)
|
||||
}
|
||||
|
||||
func TestCanSupportStructWithSliceAll(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}}
|
||||
result, err := Search("B[].Foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal([]interface{}{"f1", "correct"}, result)
|
||||
}
|
||||
|
||||
func TestCanSupportStructWithSlicingExpression(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}}
|
||||
result, err := Search("B[:].Foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal([]interface{}{"f1", "correct"}, result)
|
||||
}
|
||||
|
||||
func TestCanSupportStructWithFilterProjection(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}}
|
||||
result, err := Search("B[? `true` ].Foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal([]interface{}{"f1", "correct"}, result)
|
||||
}
|
||||
|
||||
func TestCanSupportStructWithSlice(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}}
|
||||
result, err := Search("B[-1].Foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal("correct", result)
|
||||
}
|
||||
|
||||
func TestCanSupportStructWithOrExpressions(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := sliceType{A: "foo", C: nil}
|
||||
result, err := Search("C || A", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal("foo", result)
|
||||
}
|
||||
|
||||
func TestCanSupportStructWithSlicePointer(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := sliceType{A: "foo", C: []*scalars{{"f1", "b1"}, {"correct", "b2"}}}
|
||||
result, err := Search("C[-1].Foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal("correct", result)
|
||||
}
|
||||
|
||||
func TestWillAutomaticallyCapitalizeFieldNames(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
s := scalars{Foo: "one", Bar: "bar"}
|
||||
// Note that there's a lower cased "foo" instead of "Foo",
|
||||
// but it should still correspond to the Foo field in the
|
||||
// scalars struct
|
||||
result, err := Search("foo", &s)
|
||||
assert.Nil(err)
|
||||
assert.Equal("one", result)
|
||||
}
|
||||
|
||||
func TestCanSupportStructWithSliceLowerCased(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := sliceType{A: "foo", B: []scalars{{"f1", "b1"}, {"correct", "b2"}}}
|
||||
result, err := Search("b[-1].foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal("correct", result)
|
||||
}
|
||||
|
||||
func TestCanSupportStructWithNestedPointers(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := struct{ A *struct{ B int } }{}
|
||||
result, err := Search("A.B", data)
|
||||
assert.Nil(err)
|
||||
assert.Nil(result)
|
||||
}
|
||||
|
||||
func TestCanSupportFlattenNestedSlice(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := nestedSlice{A: []sliceType{
|
||||
{B: []scalars{{Foo: "f1a"}, {Foo: "f1b"}}},
|
||||
{B: []scalars{{Foo: "f2a"}, {Foo: "f2b"}}},
|
||||
}}
|
||||
result, err := Search("A[].B[].Foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal([]interface{}{"f1a", "f1b", "f2a", "f2b"}, result)
|
||||
}
|
||||
|
||||
func TestCanSupportFlattenNestedEmptySlice(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := nestedSlice{A: []sliceType{
|
||||
{}, {B: []scalars{{Foo: "a"}}},
|
||||
}}
|
||||
result, err := Search("A[].B[].Foo", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal([]interface{}{"a"}, result)
|
||||
}
|
||||
|
||||
func TestCanSupportProjectionsWithStructs(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := nestedSlice{A: []sliceType{
|
||||
{A: "first"}, {A: "second"}, {A: "third"},
|
||||
}}
|
||||
result, err := Search("A[*].A", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal([]interface{}{"first", "second", "third"}, result)
|
||||
}
|
||||
|
||||
func TestCanSupportSliceOfStructsWithFunctions(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
data := []scalars{scalars{"a1", "b1"}, scalars{"a2", "b2"}}
|
||||
result, err := Search("length(@)", data)
|
||||
assert.Nil(err)
|
||||
assert.Equal(result.(float64), 2.0)
|
||||
}
|
||||
|
||||
func BenchmarkInterpretSingleFieldStruct(b *testing.B) {
|
||||
intr := newInterpreter()
|
||||
parser := NewParser()
|
||||
ast, _ := parser.Parse("fooasdfasdfasdfasdf")
|
||||
data := benchmarkStruct{"foobarbazqux"}
|
||||
for i := 0; i < b.N; i++ {
|
||||
intr.Execute(ast, &data)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInterpretNestedStruct(b *testing.B) {
|
||||
intr := newInterpreter()
|
||||
parser := NewParser()
|
||||
ast, _ := parser.Parse("fooasdfasdfasdfasdf.fooasdfasdfasdfasdf.fooasdfasdfasdfasdf.fooasdfasdfasdfasdf")
|
||||
data := benchmarkNested{
|
||||
nestedA{
|
||||
nestedB{
|
||||
nestedC{"foobarbazqux"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
intr.Execute(ast, &data)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInterpretNestedMaps(b *testing.B) {
|
||||
jsonData := []byte(`{"fooasdfasdfasdfasdf": {"fooasdfasdfasdfasdf": {"fooasdfasdfasdfasdf": {"fooasdfasdfasdfasdf": "foobarbazqux"}}}}`)
|
||||
var data interface{}
|
||||
json.Unmarshal(jsonData, &data)
|
||||
|
||||
intr := newInterpreter()
|
||||
parser := NewParser()
|
||||
ast, _ := parser.Parse("fooasdfasdfasdfasdf.fooasdfasdfasdfasdf.fooasdfasdfasdfasdf.fooasdfasdfasdfasdf")
|
||||
for i := 0; i < b.N; i++ {
|
||||
intr.Execute(ast, data)
|
||||
}
|
||||
}
|
420
vendor/github.com/jmespath/go-jmespath/lexer.go
generated
vendored
Normal file
420
vendor/github.com/jmespath/go-jmespath/lexer.go
generated
vendored
Normal file
|
@ -0,0 +1,420 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type token struct {
|
||||
tokenType tokType
|
||||
value string
|
||||
position int
|
||||
length int
|
||||
}
|
||||
|
||||
type tokType int
|
||||
|
||||
const eof = -1
|
||||
|
||||
// Lexer contains information about the expression being tokenized.
|
||||
type Lexer struct {
|
||||
expression string // The expression provided by the user.
|
||||
currentPos int // The current position in the string.
|
||||
lastWidth int // The width of the current rune. This
|
||||
buf bytes.Buffer // Internal buffer used for building up values.
|
||||
}
|
||||
|
||||
// SyntaxError is the main error used whenever a lexing or parsing error occurs.
|
||||
type SyntaxError struct {
|
||||
msg string // Error message displayed to user
|
||||
Expression string // Expression that generated a SyntaxError
|
||||
Offset int // The location in the string where the error occurred
|
||||
}
|
||||
|
||||
func (e SyntaxError) Error() string {
|
||||
// In the future, it would be good to underline the specific
|
||||
// location where the error occurred.
|
||||
return "SyntaxError: " + e.msg
|
||||
}
|
||||
|
||||
// HighlightLocation will show where the syntax error occurred.
|
||||
// It will place a "^" character on a line below the expression
|
||||
// at the point where the syntax error occurred.
|
||||
func (e SyntaxError) HighlightLocation() string {
|
||||
return e.Expression + "\n" + strings.Repeat(" ", e.Offset) + "^"
|
||||
}
|
||||
|
||||
//go:generate stringer -type=tokType
|
||||
const (
|
||||
tUnknown tokType = iota
|
||||
tStar
|
||||
tDot
|
||||
tFilter
|
||||
tFlatten
|
||||
tLparen
|
||||
tRparen
|
||||
tLbracket
|
||||
tRbracket
|
||||
tLbrace
|
||||
tRbrace
|
||||
tOr
|
||||
tPipe
|
||||
tNumber
|
||||
tUnquotedIdentifier
|
||||
tQuotedIdentifier
|
||||
tComma
|
||||
tColon
|
||||
tLT
|
||||
tLTE
|
||||
tGT
|
||||
tGTE
|
||||
tEQ
|
||||
tNE
|
||||
tJSONLiteral
|
||||
tStringLiteral
|
||||
tCurrent
|
||||
tExpref
|
||||
tAnd
|
||||
tNot
|
||||
tEOF
|
||||
)
|
||||
|
||||
var basicTokens = map[rune]tokType{
|
||||
'.': tDot,
|
||||
'*': tStar,
|
||||
',': tComma,
|
||||
':': tColon,
|
||||
'{': tLbrace,
|
||||
'}': tRbrace,
|
||||
']': tRbracket, // tLbracket not included because it could be "[]"
|
||||
'(': tLparen,
|
||||
')': tRparen,
|
||||
'@': tCurrent,
|
||||
}
|
||||
|
||||
// Bit mask for [a-zA-Z_] shifted down 64 bits to fit in a single uint64.
|
||||
// When using this bitmask just be sure to shift the rune down 64 bits
|
||||
// before checking against identifierStartBits.
|
||||
const identifierStartBits uint64 = 576460745995190270
|
||||
|
||||
// Bit mask for [a-zA-Z0-9], 128 bits -> 2 uint64s.
|
||||
var identifierTrailingBits = [2]uint64{287948901175001088, 576460745995190270}
|
||||
|
||||
var whiteSpace = map[rune]bool{
|
||||
' ': true, '\t': true, '\n': true, '\r': true,
|
||||
}
|
||||
|
||||
func (t token) String() string {
|
||||
return fmt.Sprintf("Token{%+v, %s, %d, %d}",
|
||||
t.tokenType, t.value, t.position, t.length)
|
||||
}
|
||||
|
||||
// NewLexer creates a new JMESPath lexer.
|
||||
func NewLexer() *Lexer {
|
||||
lexer := Lexer{}
|
||||
return &lexer
|
||||
}
|
||||
|
||||
func (lexer *Lexer) next() rune {
|
||||
if lexer.currentPos >= len(lexer.expression) {
|
||||
lexer.lastWidth = 0
|
||||
return eof
|
||||
}
|
||||
r, w := utf8.DecodeRuneInString(lexer.expression[lexer.currentPos:])
|
||||
lexer.lastWidth = w
|
||||
lexer.currentPos += w
|
||||
return r
|
||||
}
|
||||
|
||||
func (lexer *Lexer) back() {
|
||||
lexer.currentPos -= lexer.lastWidth
|
||||
}
|
||||
|
||||
func (lexer *Lexer) peek() rune {
|
||||
t := lexer.next()
|
||||
lexer.back()
|
||||
return t
|
||||
}
|
||||
|
||||
// tokenize takes an expression and returns corresponding tokens.
|
||||
func (lexer *Lexer) tokenize(expression string) ([]token, error) {
|
||||
var tokens []token
|
||||
lexer.expression = expression
|
||||
lexer.currentPos = 0
|
||||
lexer.lastWidth = 0
|
||||
loop:
|
||||
for {
|
||||
r := lexer.next()
|
||||
if identifierStartBits&(1<<(uint64(r)-64)) > 0 {
|
||||
t := lexer.consumeUnquotedIdentifier()
|
||||
tokens = append(tokens, t)
|
||||
} else if val, ok := basicTokens[r]; ok {
|
||||
// Basic single char token.
|
||||
t := token{
|
||||
tokenType: val,
|
||||
value: string(r),
|
||||
position: lexer.currentPos - lexer.lastWidth,
|
||||
length: 1,
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '-' || (r >= '0' && r <= '9') {
|
||||
t := lexer.consumeNumber()
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '[' {
|
||||
t := lexer.consumeLBracket()
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '"' {
|
||||
t, err := lexer.consumeQuotedIdentifier()
|
||||
if err != nil {
|
||||
return tokens, err
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '\'' {
|
||||
t, err := lexer.consumeRawStringLiteral()
|
||||
if err != nil {
|
||||
return tokens, err
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '`' {
|
||||
t, err := lexer.consumeLiteral()
|
||||
if err != nil {
|
||||
return tokens, err
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '|' {
|
||||
t := lexer.matchOrElse(r, '|', tOr, tPipe)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '<' {
|
||||
t := lexer.matchOrElse(r, '=', tLTE, tLT)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '>' {
|
||||
t := lexer.matchOrElse(r, '=', tGTE, tGT)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '!' {
|
||||
t := lexer.matchOrElse(r, '=', tNE, tNot)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '=' {
|
||||
t := lexer.matchOrElse(r, '=', tEQ, tUnknown)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == '&' {
|
||||
t := lexer.matchOrElse(r, '&', tAnd, tExpref)
|
||||
tokens = append(tokens, t)
|
||||
} else if r == eof {
|
||||
break loop
|
||||
} else if _, ok := whiteSpace[r]; ok {
|
||||
// Ignore whitespace
|
||||
} else {
|
||||
return tokens, lexer.syntaxError(fmt.Sprintf("Unknown char: %s", strconv.QuoteRuneToASCII(r)))
|
||||
}
|
||||
}
|
||||
tokens = append(tokens, token{tEOF, "", len(lexer.expression), 0})
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
// Consume characters until the ending rune "r" is reached.
|
||||
// If the end of the expression is reached before seeing the
|
||||
// terminating rune "r", then an error is returned.
|
||||
// If no error occurs then the matching substring is returned.
|
||||
// The returned string will not include the ending rune.
|
||||
func (lexer *Lexer) consumeUntil(end rune) (string, error) {
|
||||
start := lexer.currentPos
|
||||
current := lexer.next()
|
||||
for current != end && current != eof {
|
||||
if current == '\\' && lexer.peek() != eof {
|
||||
lexer.next()
|
||||
}
|
||||
current = lexer.next()
|
||||
}
|
||||
if lexer.lastWidth == 0 {
|
||||
// Then we hit an EOF so we never reached the closing
|
||||
// delimiter.
|
||||
return "", SyntaxError{
|
||||
msg: "Unclosed delimiter: " + string(end),
|
||||
Expression: lexer.expression,
|
||||
Offset: len(lexer.expression),
|
||||
}
|
||||
}
|
||||
return lexer.expression[start : lexer.currentPos-lexer.lastWidth], nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeLiteral() (token, error) {
|
||||
start := lexer.currentPos
|
||||
value, err := lexer.consumeUntil('`')
|
||||
if err != nil {
|
||||
return token{}, err
|
||||
}
|
||||
value = strings.Replace(value, "\\`", "`", -1)
|
||||
return token{
|
||||
tokenType: tJSONLiteral,
|
||||
value: value,
|
||||
position: start,
|
||||
length: len(value),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeRawStringLiteral() (token, error) {
|
||||
start := lexer.currentPos
|
||||
currentIndex := start
|
||||
current := lexer.next()
|
||||
for current != '\'' && lexer.peek() != eof {
|
||||
if current == '\\' && lexer.peek() == '\'' {
|
||||
chunk := lexer.expression[currentIndex : lexer.currentPos-1]
|
||||
lexer.buf.WriteString(chunk)
|
||||
lexer.buf.WriteString("'")
|
||||
lexer.next()
|
||||
currentIndex = lexer.currentPos
|
||||
}
|
||||
current = lexer.next()
|
||||
}
|
||||
if lexer.lastWidth == 0 {
|
||||
// Then we hit an EOF so we never reached the closing
|
||||
// delimiter.
|
||||
return token{}, SyntaxError{
|
||||
msg: "Unclosed delimiter: '",
|
||||
Expression: lexer.expression,
|
||||
Offset: len(lexer.expression),
|
||||
}
|
||||
}
|
||||
if currentIndex < lexer.currentPos {
|
||||
lexer.buf.WriteString(lexer.expression[currentIndex : lexer.currentPos-1])
|
||||
}
|
||||
value := lexer.buf.String()
|
||||
// Reset the buffer so it can reused again.
|
||||
lexer.buf.Reset()
|
||||
return token{
|
||||
tokenType: tStringLiteral,
|
||||
value: value,
|
||||
position: start,
|
||||
length: len(value),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) syntaxError(msg string) SyntaxError {
|
||||
return SyntaxError{
|
||||
msg: msg,
|
||||
Expression: lexer.expression,
|
||||
Offset: lexer.currentPos - 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks for a two char token, otherwise matches a single character
|
||||
// token. This is used whenever a two char token overlaps a single
|
||||
// char token, e.g. "||" -> tPipe, "|" -> tOr.
|
||||
func (lexer *Lexer) matchOrElse(first rune, second rune, matchedType tokType, singleCharType tokType) token {
|
||||
start := lexer.currentPos - lexer.lastWidth
|
||||
nextRune := lexer.next()
|
||||
var t token
|
||||
if nextRune == second {
|
||||
t = token{
|
||||
tokenType: matchedType,
|
||||
value: string(first) + string(second),
|
||||
position: start,
|
||||
length: 2,
|
||||
}
|
||||
} else {
|
||||
lexer.back()
|
||||
t = token{
|
||||
tokenType: singleCharType,
|
||||
value: string(first),
|
||||
position: start,
|
||||
length: 1,
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeLBracket() token {
|
||||
// There's three options here:
|
||||
// 1. A filter expression "[?"
|
||||
// 2. A flatten operator "[]"
|
||||
// 3. A bare rbracket "["
|
||||
start := lexer.currentPos - lexer.lastWidth
|
||||
nextRune := lexer.next()
|
||||
var t token
|
||||
if nextRune == '?' {
|
||||
t = token{
|
||||
tokenType: tFilter,
|
||||
value: "[?",
|
||||
position: start,
|
||||
length: 2,
|
||||
}
|
||||
} else if nextRune == ']' {
|
||||
t = token{
|
||||
tokenType: tFlatten,
|
||||
value: "[]",
|
||||
position: start,
|
||||
length: 2,
|
||||
}
|
||||
} else {
|
||||
t = token{
|
||||
tokenType: tLbracket,
|
||||
value: "[",
|
||||
position: start,
|
||||
length: 1,
|
||||
}
|
||||
lexer.back()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeQuotedIdentifier() (token, error) {
|
||||
start := lexer.currentPos
|
||||
value, err := lexer.consumeUntil('"')
|
||||
if err != nil {
|
||||
return token{}, err
|
||||
}
|
||||
var decoded string
|
||||
asJSON := []byte("\"" + value + "\"")
|
||||
if err := json.Unmarshal([]byte(asJSON), &decoded); err != nil {
|
||||
return token{}, err
|
||||
}
|
||||
return token{
|
||||
tokenType: tQuotedIdentifier,
|
||||
value: decoded,
|
||||
position: start - 1,
|
||||
length: len(decoded),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeUnquotedIdentifier() token {
|
||||
// Consume runes until we reach the end of an unquoted
|
||||
// identifier.
|
||||
start := lexer.currentPos - lexer.lastWidth
|
||||
for {
|
||||
r := lexer.next()
|
||||
if r < 0 || r > 128 || identifierTrailingBits[uint64(r)/64]&(1<<(uint64(r)%64)) == 0 {
|
||||
lexer.back()
|
||||
break
|
||||
}
|
||||
}
|
||||
value := lexer.expression[start:lexer.currentPos]
|
||||
return token{
|
||||
tokenType: tUnquotedIdentifier,
|
||||
value: value,
|
||||
position: start,
|
||||
length: lexer.currentPos - start,
|
||||
}
|
||||
}
|
||||
|
||||
func (lexer *Lexer) consumeNumber() token {
|
||||
// Consume runes until we reach something that's not a number.
|
||||
start := lexer.currentPos - lexer.lastWidth
|
||||
for {
|
||||
r := lexer.next()
|
||||
if r < '0' || r > '9' {
|
||||
lexer.back()
|
||||
break
|
||||
}
|
||||
}
|
||||
value := lexer.expression[start:lexer.currentPos]
|
||||
return token{
|
||||
tokenType: tNumber,
|
||||
value: value,
|
||||
position: start,
|
||||
length: lexer.currentPos - start,
|
||||
}
|
||||
}
|
161
vendor/github.com/jmespath/go-jmespath/lexer_test.go
generated
vendored
Normal file
161
vendor/github.com/jmespath/go-jmespath/lexer_test.go
generated
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var lexingTests = []struct {
|
||||
expression string
|
||||
expected []token
|
||||
}{
|
||||
{"*", []token{{tStar, "*", 0, 1}}},
|
||||
{".", []token{{tDot, ".", 0, 1}}},
|
||||
{"[?", []token{{tFilter, "[?", 0, 2}}},
|
||||
{"[]", []token{{tFlatten, "[]", 0, 2}}},
|
||||
{"(", []token{{tLparen, "(", 0, 1}}},
|
||||
{")", []token{{tRparen, ")", 0, 1}}},
|
||||
{"[", []token{{tLbracket, "[", 0, 1}}},
|
||||
{"]", []token{{tRbracket, "]", 0, 1}}},
|
||||
{"{", []token{{tLbrace, "{", 0, 1}}},
|
||||
{"}", []token{{tRbrace, "}", 0, 1}}},
|
||||
{"||", []token{{tOr, "||", 0, 2}}},
|
||||
{"|", []token{{tPipe, "|", 0, 1}}},
|
||||
{"29", []token{{tNumber, "29", 0, 2}}},
|
||||
{"2", []token{{tNumber, "2", 0, 1}}},
|
||||
{"0", []token{{tNumber, "0", 0, 1}}},
|
||||
{"-20", []token{{tNumber, "-20", 0, 3}}},
|
||||
{"foo", []token{{tUnquotedIdentifier, "foo", 0, 3}}},
|
||||
{`"bar"`, []token{{tQuotedIdentifier, "bar", 0, 3}}},
|
||||
// Escaping the delimiter
|
||||
{`"bar\"baz"`, []token{{tQuotedIdentifier, `bar"baz`, 0, 7}}},
|
||||
{",", []token{{tComma, ",", 0, 1}}},
|
||||
{":", []token{{tColon, ":", 0, 1}}},
|
||||
{"<", []token{{tLT, "<", 0, 1}}},
|
||||
{"<=", []token{{tLTE, "<=", 0, 2}}},
|
||||
{">", []token{{tGT, ">", 0, 1}}},
|
||||
{">=", []token{{tGTE, ">=", 0, 2}}},
|
||||
{"==", []token{{tEQ, "==", 0, 2}}},
|
||||
{"!=", []token{{tNE, "!=", 0, 2}}},
|
||||
{"`[0, 1, 2]`", []token{{tJSONLiteral, "[0, 1, 2]", 1, 9}}},
|
||||
{"'foo'", []token{{tStringLiteral, "foo", 1, 3}}},
|
||||
{"'a'", []token{{tStringLiteral, "a", 1, 1}}},
|
||||
{`'foo\'bar'`, []token{{tStringLiteral, "foo'bar", 1, 7}}},
|
||||
{"@", []token{{tCurrent, "@", 0, 1}}},
|
||||
{"&", []token{{tExpref, "&", 0, 1}}},
|
||||
// Quoted identifier unicode escape sequences
|
||||
{`"\u2713"`, []token{{tQuotedIdentifier, "✓", 0, 3}}},
|
||||
{`"\\"`, []token{{tQuotedIdentifier, `\`, 0, 1}}},
|
||||
{"`\"foo\"`", []token{{tJSONLiteral, "\"foo\"", 1, 5}}},
|
||||
// Combinations of tokens.
|
||||
{"foo.bar", []token{
|
||||
{tUnquotedIdentifier, "foo", 0, 3},
|
||||
{tDot, ".", 3, 1},
|
||||
{tUnquotedIdentifier, "bar", 4, 3},
|
||||
}},
|
||||
{"foo[0]", []token{
|
||||
{tUnquotedIdentifier, "foo", 0, 3},
|
||||
{tLbracket, "[", 3, 1},
|
||||
{tNumber, "0", 4, 1},
|
||||
{tRbracket, "]", 5, 1},
|
||||
}},
|
||||
{"foo[?a<b]", []token{
|
||||
{tUnquotedIdentifier, "foo", 0, 3},
|
||||
{tFilter, "[?", 3, 2},
|
||||
{tUnquotedIdentifier, "a", 5, 1},
|
||||
{tLT, "<", 6, 1},
|
||||
{tUnquotedIdentifier, "b", 7, 1},
|
||||
{tRbracket, "]", 8, 1},
|
||||
}},
|
||||
}
|
||||
|
||||
func TestCanLexTokens(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
lexer := NewLexer()
|
||||
for _, tt := range lexingTests {
|
||||
tokens, err := lexer.tokenize(tt.expression)
|
||||
if assert.Nil(err) {
|
||||
errMsg := fmt.Sprintf("Mismatch expected number of tokens: (expected: %s, actual: %s)",
|
||||
tt.expected, tokens)
|
||||
tt.expected = append(tt.expected, token{tEOF, "", len(tt.expression), 0})
|
||||
if assert.Equal(len(tt.expected), len(tokens), errMsg) {
|
||||
for i, token := range tokens {
|
||||
expected := tt.expected[i]
|
||||
assert.Equal(expected, token, "Token not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lexingErrorTests = []struct {
|
||||
expression string
|
||||
msg string
|
||||
}{
|
||||
{"'foo", "Missing closing single quote"},
|
||||
{"[?foo==bar?]", "Unknown char '?'"},
|
||||
}
|
||||
|
||||
func TestLexingErrors(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
lexer := NewLexer()
|
||||
for _, tt := range lexingErrorTests {
|
||||
_, err := lexer.tokenize(tt.expression)
|
||||
assert.NotNil(err, fmt.Sprintf("Expected lexing error: %s", tt.msg))
|
||||
}
|
||||
}
|
||||
|
||||
var exprIdentifier = "abcdefghijklmnopqrstuvwxyz"
|
||||
var exprSubexpr = "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz"
|
||||
var deeplyNested50 = "j49.j48.j47.j46.j45.j44.j43.j42.j41.j40.j39.j38.j37.j36.j35.j34.j33.j32.j31.j30.j29.j28.j27.j26.j25.j24.j23.j22.j21.j20.j19.j18.j17.j16.j15.j14.j13.j12.j11.j10.j9.j8.j7.j6.j5.j4.j3.j2.j1.j0"
|
||||
var deeplyNested50Pipe = "j49|j48|j47|j46|j45|j44|j43|j42|j41|j40|j39|j38|j37|j36|j35|j34|j33|j32|j31|j30|j29|j28|j27|j26|j25|j24|j23|j22|j21|j20|j19|j18|j17|j16|j15|j14|j13|j12|j11|j10|j9|j8|j7|j6|j5|j4|j3|j2|j1|j0"
|
||||
var deeplyNested50Index = "[49][48][47][46][45][44][43][42][41][40][39][38][37][36][35][34][33][32][31][30][29][28][27][26][25][24][23][22][21][20][19][18][17][16][15][14][13][12][11][10][9][8][7][6][5][4][3][2][1][0]"
|
||||
var deepProjection104 = "a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*].a[*].b[*].c[*].d[*].e[*].f[*].g[*].h[*].i[*].j[*].k[*].l[*].m[*].n[*].o[*].p[*].q[*].r[*].s[*].t[*].u[*].v[*].w[*].x[*].y[*].z[*]"
|
||||
var exprQuotedIdentifier = `"abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz"`
|
||||
var quotedIdentifierEscapes = `"\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t\n\r\b\t"`
|
||||
var rawStringLiteral = `'abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz'`
|
||||
|
||||
func BenchmarkLexIdentifier(b *testing.B) {
|
||||
runLexBenchmark(b, exprIdentifier)
|
||||
}
|
||||
|
||||
func BenchmarkLexSubexpression(b *testing.B) {
|
||||
runLexBenchmark(b, exprSubexpr)
|
||||
}
|
||||
|
||||
func BenchmarkLexDeeplyNested50(b *testing.B) {
|
||||
runLexBenchmark(b, deeplyNested50)
|
||||
}
|
||||
|
||||
func BenchmarkLexDeepNested50Pipe(b *testing.B) {
|
||||
runLexBenchmark(b, deeplyNested50Pipe)
|
||||
}
|
||||
|
||||
func BenchmarkLexDeepNested50Index(b *testing.B) {
|
||||
runLexBenchmark(b, deeplyNested50Index)
|
||||
}
|
||||
|
||||
func BenchmarkLexQuotedIdentifier(b *testing.B) {
|
||||
runLexBenchmark(b, exprQuotedIdentifier)
|
||||
}
|
||||
|
||||
func BenchmarkLexQuotedIdentifierEscapes(b *testing.B) {
|
||||
runLexBenchmark(b, quotedIdentifierEscapes)
|
||||
}
|
||||
|
||||
func BenchmarkLexRawStringLiteral(b *testing.B) {
|
||||
runLexBenchmark(b, rawStringLiteral)
|
||||
}
|
||||
|
||||
func BenchmarkLexDeepProjection104(b *testing.B) {
|
||||
runLexBenchmark(b, deepProjection104)
|
||||
}
|
||||
|
||||
func runLexBenchmark(b *testing.B, expression string) {
|
||||
lexer := NewLexer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lexer.tokenize(expression)
|
||||
}
|
||||
}
|
603
vendor/github.com/jmespath/go-jmespath/parser.go
generated
vendored
Normal file
603
vendor/github.com/jmespath/go-jmespath/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,603 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type astNodeType int
|
||||
|
||||
//go:generate stringer -type astNodeType
|
||||
const (
|
||||
ASTEmpty astNodeType = iota
|
||||
ASTComparator
|
||||
ASTCurrentNode
|
||||
ASTExpRef
|
||||
ASTFunctionExpression
|
||||
ASTField
|
||||
ASTFilterProjection
|
||||
ASTFlatten
|
||||
ASTIdentity
|
||||
ASTIndex
|
||||
ASTIndexExpression
|
||||
ASTKeyValPair
|
||||
ASTLiteral
|
||||
ASTMultiSelectHash
|
||||
ASTMultiSelectList
|
||||
ASTOrExpression
|
||||
ASTAndExpression
|
||||
ASTNotExpression
|
||||
ASTPipe
|
||||
ASTProjection
|
||||
ASTSubexpression
|
||||
ASTSlice
|
||||
ASTValueProjection
|
||||
)
|
||||
|
||||
// ASTNode represents the abstract syntax tree of a JMESPath expression.
|
||||
type ASTNode struct {
|
||||
nodeType astNodeType
|
||||
value interface{}
|
||||
children []ASTNode
|
||||
}
|
||||
|
||||
func (node ASTNode) String() string {
|
||||
return node.PrettyPrint(0)
|
||||
}
|
||||
|
||||
// PrettyPrint will pretty print the parsed AST.
|
||||
// The AST is an implementation detail and this pretty print
|
||||
// function is provided as a convenience method to help with
|
||||
// debugging. You should not rely on its output as the internal
|
||||
// structure of the AST may change at any time.
|
||||
func (node ASTNode) PrettyPrint(indent int) string {
|
||||
spaces := strings.Repeat(" ", indent)
|
||||
output := fmt.Sprintf("%s%s {\n", spaces, node.nodeType)
|
||||
nextIndent := indent + 2
|
||||
if node.value != nil {
|
||||
if converted, ok := node.value.(fmt.Stringer); ok {
|
||||
// Account for things like comparator nodes
|
||||
// that are enums with a String() method.
|
||||
output += fmt.Sprintf("%svalue: %s\n", strings.Repeat(" ", nextIndent), converted.String())
|
||||
} else {
|
||||
output += fmt.Sprintf("%svalue: %#v\n", strings.Repeat(" ", nextIndent), node.value)
|
||||
}
|
||||
}
|
||||
lastIndex := len(node.children)
|
||||
if lastIndex > 0 {
|
||||
output += fmt.Sprintf("%schildren: {\n", strings.Repeat(" ", nextIndent))
|
||||
childIndent := nextIndent + 2
|
||||
for _, elem := range node.children {
|
||||
output += elem.PrettyPrint(childIndent)
|
||||
}
|
||||
}
|
||||
output += fmt.Sprintf("%s}\n", spaces)
|
||||
return output
|
||||
}
|
||||
|
||||
var bindingPowers = map[tokType]int{
|
||||
tEOF: 0,
|
||||
tUnquotedIdentifier: 0,
|
||||
tQuotedIdentifier: 0,
|
||||
tRbracket: 0,
|
||||
tRparen: 0,
|
||||
tComma: 0,
|
||||
tRbrace: 0,
|
||||
tNumber: 0,
|
||||
tCurrent: 0,
|
||||
tExpref: 0,
|
||||
tColon: 0,
|
||||
tPipe: 1,
|
||||
tOr: 2,
|
||||
tAnd: 3,
|
||||
tEQ: 5,
|
||||
tLT: 5,
|
||||
tLTE: 5,
|
||||
tGT: 5,
|
||||
tGTE: 5,
|
||||
tNE: 5,
|
||||
tFlatten: 9,
|
||||
tStar: 20,
|
||||
tFilter: 21,
|
||||
tDot: 40,
|
||||
tNot: 45,
|
||||
tLbrace: 50,
|
||||
tLbracket: 55,
|
||||
tLparen: 60,
|
||||
}
|
||||
|
||||
// Parser holds state about the current expression being parsed.
|
||||
type Parser struct {
|
||||
expression string
|
||||
tokens []token
|
||||
index int
|
||||
}
|
||||
|
||||
// NewParser creates a new JMESPath parser.
|
||||
func NewParser() *Parser {
|
||||
p := Parser{}
|
||||
return &p
|
||||
}
|
||||
|
||||
// Parse will compile a JMESPath expression.
|
||||
func (p *Parser) Parse(expression string) (ASTNode, error) {
|
||||
lexer := NewLexer()
|
||||
p.expression = expression
|
||||
p.index = 0
|
||||
tokens, err := lexer.tokenize(expression)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
p.tokens = tokens
|
||||
parsed, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if p.current() != tEOF {
|
||||
return ASTNode{}, p.syntaxError(fmt.Sprintf(
|
||||
"Unexpected token at the end of the expresssion: %s", p.current()))
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseExpression(bindingPower int) (ASTNode, error) {
|
||||
var err error
|
||||
leftToken := p.lookaheadToken(0)
|
||||
p.advance()
|
||||
leftNode, err := p.nud(leftToken)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
currentToken := p.current()
|
||||
for bindingPower < bindingPowers[currentToken] {
|
||||
p.advance()
|
||||
leftNode, err = p.led(currentToken, leftNode)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
currentToken = p.current()
|
||||
}
|
||||
return leftNode, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseIndexExpression() (ASTNode, error) {
|
||||
if p.lookahead(0) == tColon || p.lookahead(1) == tColon {
|
||||
return p.parseSliceExpression()
|
||||
}
|
||||
indexStr := p.lookaheadToken(0).value
|
||||
parsedInt, err := strconv.Atoi(indexStr)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
indexNode := ASTNode{nodeType: ASTIndex, value: parsedInt}
|
||||
p.advance()
|
||||
if err := p.match(tRbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return indexNode, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseSliceExpression() (ASTNode, error) {
|
||||
parts := []*int{nil, nil, nil}
|
||||
index := 0
|
||||
current := p.current()
|
||||
for current != tRbracket && index < 3 {
|
||||
if current == tColon {
|
||||
index++
|
||||
p.advance()
|
||||
} else if current == tNumber {
|
||||
parsedInt, err := strconv.Atoi(p.lookaheadToken(0).value)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
parts[index] = &parsedInt
|
||||
p.advance()
|
||||
} else {
|
||||
return ASTNode{}, p.syntaxError(
|
||||
"Expected tColon or tNumber" + ", received: " + p.current().String())
|
||||
}
|
||||
current = p.current()
|
||||
}
|
||||
if err := p.match(tRbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTSlice,
|
||||
value: parts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) match(tokenType tokType) error {
|
||||
if p.current() == tokenType {
|
||||
p.advance()
|
||||
return nil
|
||||
}
|
||||
return p.syntaxError("Expected " + tokenType.String() + ", received: " + p.current().String())
|
||||
}
|
||||
|
||||
func (p *Parser) led(tokenType tokType, node ASTNode) (ASTNode, error) {
|
||||
switch tokenType {
|
||||
case tDot:
|
||||
if p.current() != tStar {
|
||||
right, err := p.parseDotRHS(bindingPowers[tDot])
|
||||
return ASTNode{
|
||||
nodeType: ASTSubexpression,
|
||||
children: []ASTNode{node, right},
|
||||
}, err
|
||||
}
|
||||
p.advance()
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tDot])
|
||||
return ASTNode{
|
||||
nodeType: ASTValueProjection,
|
||||
children: []ASTNode{node, right},
|
||||
}, err
|
||||
case tPipe:
|
||||
right, err := p.parseExpression(bindingPowers[tPipe])
|
||||
return ASTNode{nodeType: ASTPipe, children: []ASTNode{node, right}}, err
|
||||
case tOr:
|
||||
right, err := p.parseExpression(bindingPowers[tOr])
|
||||
return ASTNode{nodeType: ASTOrExpression, children: []ASTNode{node, right}}, err
|
||||
case tAnd:
|
||||
right, err := p.parseExpression(bindingPowers[tAnd])
|
||||
return ASTNode{nodeType: ASTAndExpression, children: []ASTNode{node, right}}, err
|
||||
case tLparen:
|
||||
name := node.value
|
||||
var args []ASTNode
|
||||
for p.current() != tRparen {
|
||||
expression, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if p.current() == tComma {
|
||||
if err := p.match(tComma); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
}
|
||||
args = append(args, expression)
|
||||
}
|
||||
if err := p.match(tRparen); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTFunctionExpression,
|
||||
value: name,
|
||||
children: args,
|
||||
}, nil
|
||||
case tFilter:
|
||||
return p.parseFilter(node)
|
||||
case tFlatten:
|
||||
left := ASTNode{nodeType: ASTFlatten, children: []ASTNode{node}}
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tFlatten])
|
||||
return ASTNode{
|
||||
nodeType: ASTProjection,
|
||||
children: []ASTNode{left, right},
|
||||
}, err
|
||||
case tEQ, tNE, tGT, tGTE, tLT, tLTE:
|
||||
right, err := p.parseExpression(bindingPowers[tokenType])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTComparator,
|
||||
value: tokenType,
|
||||
children: []ASTNode{node, right},
|
||||
}, nil
|
||||
case tLbracket:
|
||||
tokenType := p.current()
|
||||
var right ASTNode
|
||||
var err error
|
||||
if tokenType == tNumber || tokenType == tColon {
|
||||
right, err = p.parseIndexExpression()
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return p.projectIfSlice(node, right)
|
||||
}
|
||||
// Otherwise this is a projection.
|
||||
if err := p.match(tStar); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if err := p.match(tRbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
right, err = p.parseProjectionRHS(bindingPowers[tStar])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTProjection,
|
||||
children: []ASTNode{node, right},
|
||||
}, nil
|
||||
}
|
||||
return ASTNode{}, p.syntaxError("Unexpected token: " + tokenType.String())
|
||||
}
|
||||
|
||||
func (p *Parser) nud(token token) (ASTNode, error) {
|
||||
switch token.tokenType {
|
||||
case tJSONLiteral:
|
||||
var parsed interface{}
|
||||
err := json.Unmarshal([]byte(token.value), &parsed)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{nodeType: ASTLiteral, value: parsed}, nil
|
||||
case tStringLiteral:
|
||||
return ASTNode{nodeType: ASTLiteral, value: token.value}, nil
|
||||
case tUnquotedIdentifier:
|
||||
return ASTNode{
|
||||
nodeType: ASTField,
|
||||
value: token.value,
|
||||
}, nil
|
||||
case tQuotedIdentifier:
|
||||
node := ASTNode{nodeType: ASTField, value: token.value}
|
||||
if p.current() == tLparen {
|
||||
return ASTNode{}, p.syntaxErrorToken("Can't have quoted identifier as function name.", token)
|
||||
}
|
||||
return node, nil
|
||||
case tStar:
|
||||
left := ASTNode{nodeType: ASTIdentity}
|
||||
var right ASTNode
|
||||
var err error
|
||||
if p.current() == tRbracket {
|
||||
right = ASTNode{nodeType: ASTIdentity}
|
||||
} else {
|
||||
right, err = p.parseProjectionRHS(bindingPowers[tStar])
|
||||
}
|
||||
return ASTNode{nodeType: ASTValueProjection, children: []ASTNode{left, right}}, err
|
||||
case tFilter:
|
||||
return p.parseFilter(ASTNode{nodeType: ASTIdentity})
|
||||
case tLbrace:
|
||||
return p.parseMultiSelectHash()
|
||||
case tFlatten:
|
||||
left := ASTNode{
|
||||
nodeType: ASTFlatten,
|
||||
children: []ASTNode{{nodeType: ASTIdentity}},
|
||||
}
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tFlatten])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{nodeType: ASTProjection, children: []ASTNode{left, right}}, nil
|
||||
case tLbracket:
|
||||
tokenType := p.current()
|
||||
//var right ASTNode
|
||||
if tokenType == tNumber || tokenType == tColon {
|
||||
right, err := p.parseIndexExpression()
|
||||
if err != nil {
|
||||
return ASTNode{}, nil
|
||||
}
|
||||
return p.projectIfSlice(ASTNode{nodeType: ASTIdentity}, right)
|
||||
} else if tokenType == tStar && p.lookahead(1) == tRbracket {
|
||||
p.advance()
|
||||
p.advance()
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tStar])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTProjection,
|
||||
children: []ASTNode{{nodeType: ASTIdentity}, right},
|
||||
}, nil
|
||||
} else {
|
||||
return p.parseMultiSelectList()
|
||||
}
|
||||
case tCurrent:
|
||||
return ASTNode{nodeType: ASTCurrentNode}, nil
|
||||
case tExpref:
|
||||
expression, err := p.parseExpression(bindingPowers[tExpref])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{nodeType: ASTExpRef, children: []ASTNode{expression}}, nil
|
||||
case tNot:
|
||||
expression, err := p.parseExpression(bindingPowers[tNot])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{nodeType: ASTNotExpression, children: []ASTNode{expression}}, nil
|
||||
case tLparen:
|
||||
expression, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if err := p.match(tRparen); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return expression, nil
|
||||
case tEOF:
|
||||
return ASTNode{}, p.syntaxErrorToken("Incomplete expression", token)
|
||||
}
|
||||
|
||||
return ASTNode{}, p.syntaxErrorToken("Invalid token: "+token.tokenType.String(), token)
|
||||
}
|
||||
|
||||
func (p *Parser) parseMultiSelectList() (ASTNode, error) {
|
||||
var expressions []ASTNode
|
||||
for {
|
||||
expression, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
expressions = append(expressions, expression)
|
||||
if p.current() == tRbracket {
|
||||
break
|
||||
}
|
||||
err = p.match(tComma)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
}
|
||||
err := p.match(tRbracket)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTMultiSelectList,
|
||||
children: expressions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseMultiSelectHash() (ASTNode, error) {
|
||||
var children []ASTNode
|
||||
for {
|
||||
keyToken := p.lookaheadToken(0)
|
||||
if err := p.match(tUnquotedIdentifier); err != nil {
|
||||
if err := p.match(tQuotedIdentifier); err != nil {
|
||||
return ASTNode{}, p.syntaxError("Expected tQuotedIdentifier or tUnquotedIdentifier")
|
||||
}
|
||||
}
|
||||
keyName := keyToken.value
|
||||
err := p.match(tColon)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
value, err := p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
node := ASTNode{
|
||||
nodeType: ASTKeyValPair,
|
||||
value: keyName,
|
||||
children: []ASTNode{value},
|
||||
}
|
||||
children = append(children, node)
|
||||
if p.current() == tComma {
|
||||
err := p.match(tComma)
|
||||
if err != nil {
|
||||
return ASTNode{}, nil
|
||||
}
|
||||
} else if p.current() == tRbrace {
|
||||
err := p.match(tRbrace)
|
||||
if err != nil {
|
||||
return ASTNode{}, nil
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return ASTNode{
|
||||
nodeType: ASTMultiSelectHash,
|
||||
children: children,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) projectIfSlice(left ASTNode, right ASTNode) (ASTNode, error) {
|
||||
indexExpr := ASTNode{
|
||||
nodeType: ASTIndexExpression,
|
||||
children: []ASTNode{left, right},
|
||||
}
|
||||
if right.nodeType == ASTSlice {
|
||||
right, err := p.parseProjectionRHS(bindingPowers[tStar])
|
||||
return ASTNode{
|
||||
nodeType: ASTProjection,
|
||||
children: []ASTNode{indexExpr, right},
|
||||
}, err
|
||||
}
|
||||
return indexExpr, nil
|
||||
}
|
||||
func (p *Parser) parseFilter(node ASTNode) (ASTNode, error) {
|
||||
var right, condition ASTNode
|
||||
var err error
|
||||
condition, err = p.parseExpression(0)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if err := p.match(tRbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
if p.current() == tFlatten {
|
||||
right = ASTNode{nodeType: ASTIdentity}
|
||||
} else {
|
||||
right, err = p.parseProjectionRHS(bindingPowers[tFilter])
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return ASTNode{
|
||||
nodeType: ASTFilterProjection,
|
||||
children: []ASTNode{node, right, condition},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseDotRHS(bindingPower int) (ASTNode, error) {
|
||||
lookahead := p.current()
|
||||
if tokensOneOf([]tokType{tQuotedIdentifier, tUnquotedIdentifier, tStar}, lookahead) {
|
||||
return p.parseExpression(bindingPower)
|
||||
} else if lookahead == tLbracket {
|
||||
if err := p.match(tLbracket); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return p.parseMultiSelectList()
|
||||
} else if lookahead == tLbrace {
|
||||
if err := p.match(tLbrace); err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return p.parseMultiSelectHash()
|
||||
}
|
||||
return ASTNode{}, p.syntaxError("Expected identifier, lbracket, or lbrace")
|
||||
}
|
||||
|
||||
func (p *Parser) parseProjectionRHS(bindingPower int) (ASTNode, error) {
|
||||
current := p.current()
|
||||
if bindingPowers[current] < 10 {
|
||||
return ASTNode{nodeType: ASTIdentity}, nil
|
||||
} else if current == tLbracket {
|
||||
return p.parseExpression(bindingPower)
|
||||
} else if current == tFilter {
|
||||
return p.parseExpression(bindingPower)
|
||||
} else if current == tDot {
|
||||
err := p.match(tDot)
|
||||
if err != nil {
|
||||
return ASTNode{}, err
|
||||
}
|
||||
return p.parseDotRHS(bindingPower)
|
||||
} else {
|
||||
return ASTNode{}, p.syntaxError("Error")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) lookahead(number int) tokType {
|
||||
return p.lookaheadToken(number).tokenType
|
||||
}
|
||||
|
||||
func (p *Parser) current() tokType {
|
||||
return p.lookahead(0)
|
||||
}
|
||||
|
||||
func (p *Parser) lookaheadToken(number int) token {
|
||||
return p.tokens[p.index+number]
|
||||
}
|
||||
|
||||
func (p *Parser) advance() {
|
||||
p.index++
|
||||
}
|
||||
|
||||
func tokensOneOf(elements []tokType, token tokType) bool {
|
||||
for _, elem := range elements {
|
||||
if elem == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Parser) syntaxError(msg string) SyntaxError {
|
||||
return SyntaxError{
|
||||
msg: msg,
|
||||
Expression: p.expression,
|
||||
Offset: p.lookaheadToken(0).position,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a SyntaxError based on the provided token.
|
||||
// This differs from syntaxError() which creates a SyntaxError
|
||||
// based on the current lookahead token.
|
||||
func (p *Parser) syntaxErrorToken(msg string, t token) SyntaxError {
|
||||
return SyntaxError{
|
||||
msg: msg,
|
||||
Expression: p.expression,
|
||||
Offset: t.position,
|
||||
}
|
||||
}
|
136
vendor/github.com/jmespath/go-jmespath/parser_test.go
generated
vendored
Normal file
136
vendor/github.com/jmespath/go-jmespath/parser_test.go
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var parsingErrorTests = []struct {
|
||||
expression string
|
||||
msg string
|
||||
}{
|
||||
{"foo.", "Incopmlete expression"},
|
||||
{"[foo", "Incopmlete expression"},
|
||||
{"]", "Invalid"},
|
||||
{")", "Invalid"},
|
||||
{"}", "Invalid"},
|
||||
{"foo..bar", "Invalid"},
|
||||
{`foo."bar`, "Forwards lexer errors"},
|
||||
{`{foo: bar`, "Incomplete expression"},
|
||||
{`{foo bar}`, "Invalid"},
|
||||
{`[foo bar]`, "Invalid"},
|
||||
{`foo@`, "Invalid"},
|
||||
{`&&&&&&&&&&&&t(`, "Invalid"},
|
||||
{`[*][`, "Invalid"},
|
||||
}
|
||||
|
||||
func TestParsingErrors(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
parser := NewParser()
|
||||
for _, tt := range parsingErrorTests {
|
||||
_, err := parser.Parse(tt.expression)
|
||||
assert.NotNil(err, fmt.Sprintf("Expected parsing error: %s, for expression: %s", tt.msg, tt.expression))
|
||||
}
|
||||
}
|
||||
|
||||
var prettyPrinted = `ASTProjection {
|
||||
children: {
|
||||
ASTField {
|
||||
value: "foo"
|
||||
}
|
||||
ASTSubexpression {
|
||||
children: {
|
||||
ASTSubexpression {
|
||||
children: {
|
||||
ASTField {
|
||||
value: "bar"
|
||||
}
|
||||
ASTField {
|
||||
value: "baz"
|
||||
}
|
||||
}
|
||||
ASTField {
|
||||
value: "qux"
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var prettyPrintedCompNode = `ASTFilterProjection {
|
||||
children: {
|
||||
ASTField {
|
||||
value: "a"
|
||||
}
|
||||
ASTIdentity {
|
||||
}
|
||||
ASTComparator {
|
||||
value: tLTE
|
||||
children: {
|
||||
ASTField {
|
||||
value: "b"
|
||||
}
|
||||
ASTField {
|
||||
value: "c"
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
func TestPrettyPrintedAST(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
parser := NewParser()
|
||||
parsed, _ := parser.Parse("foo[*].bar.baz.qux")
|
||||
assert.Equal(parsed.PrettyPrint(0), prettyPrinted)
|
||||
}
|
||||
|
||||
func TestPrettyPrintedCompNode(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
parser := NewParser()
|
||||
parsed, _ := parser.Parse("a[?b<=c]")
|
||||
assert.Equal(parsed.PrettyPrint(0), prettyPrintedCompNode)
|
||||
}
|
||||
|
||||
func BenchmarkParseIdentifier(b *testing.B) {
|
||||
runParseBenchmark(b, exprIdentifier)
|
||||
}
|
||||
|
||||
func BenchmarkParseSubexpression(b *testing.B) {
|
||||
runParseBenchmark(b, exprSubexpr)
|
||||
}
|
||||
|
||||
func BenchmarkParseDeeplyNested50(b *testing.B) {
|
||||
runParseBenchmark(b, deeplyNested50)
|
||||
}
|
||||
|
||||
func BenchmarkParseDeepNested50Pipe(b *testing.B) {
|
||||
runParseBenchmark(b, deeplyNested50Pipe)
|
||||
}
|
||||
|
||||
func BenchmarkParseDeepNested50Index(b *testing.B) {
|
||||
runParseBenchmark(b, deeplyNested50Index)
|
||||
}
|
||||
|
||||
func BenchmarkParseQuotedIdentifier(b *testing.B) {
|
||||
runParseBenchmark(b, exprQuotedIdentifier)
|
||||
}
|
||||
|
||||
func BenchmarkParseQuotedIdentifierEscapes(b *testing.B) {
|
||||
runParseBenchmark(b, quotedIdentifierEscapes)
|
||||
}
|
||||
|
||||
func BenchmarkParseRawStringLiteral(b *testing.B) {
|
||||
runParseBenchmark(b, rawStringLiteral)
|
||||
}
|
||||
|
||||
func BenchmarkParseDeepProjection104(b *testing.B) {
|
||||
runParseBenchmark(b, deepProjection104)
|
||||
}
|
||||
|
||||
func runParseBenchmark(b *testing.B, expression string) {
|
||||
parser := NewParser()
|
||||
for i := 0; i < b.N; i++ {
|
||||
parser.Parse(expression)
|
||||
}
|
||||
}
|
16
vendor/github.com/jmespath/go-jmespath/toktype_string.go
generated
vendored
Normal file
16
vendor/github.com/jmespath/go-jmespath/toktype_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// generated by stringer -type=tokType; DO NOT EDIT
|
||||
|
||||
package jmespath
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _tokType_name = "tUnknowntStartDottFiltertFlattentLparentRparentLbrackettRbrackettLbracetRbracetOrtPipetNumbertUnquotedIdentifiertQuotedIdentifiertCommatColontLTtLTEtGTtGTEtEQtNEtJSONLiteraltStringLiteraltCurrenttExpreftAndtNottEOF"
|
||||
|
||||
var _tokType_index = [...]uint8{0, 8, 13, 17, 24, 32, 39, 46, 55, 64, 71, 78, 81, 86, 93, 112, 129, 135, 141, 144, 148, 151, 155, 158, 161, 173, 187, 195, 202, 206, 210, 214}
|
||||
|
||||
func (i tokType) String() string {
|
||||
if i < 0 || i >= tokType(len(_tokType_index)-1) {
|
||||
return fmt.Sprintf("tokType(%d)", i)
|
||||
}
|
||||
return _tokType_name[_tokType_index[i]:_tokType_index[i+1]]
|
||||
}
|
185
vendor/github.com/jmespath/go-jmespath/util.go
generated
vendored
Normal file
185
vendor/github.com/jmespath/go-jmespath/util.go
generated
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// IsFalse determines if an object is false based on the JMESPath spec.
|
||||
// JMESPath defines false values to be any of:
|
||||
// - An empty string array, or hash.
|
||||
// - The boolean value false.
|
||||
// - nil
|
||||
func isFalse(value interface{}) bool {
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
return !v
|
||||
case []interface{}:
|
||||
return len(v) == 0
|
||||
case map[string]interface{}:
|
||||
return len(v) == 0
|
||||
case string:
|
||||
return len(v) == 0
|
||||
case nil:
|
||||
return true
|
||||
}
|
||||
// Try the reflection cases before returning false.
|
||||
rv := reflect.ValueOf(value)
|
||||
switch rv.Kind() {
|
||||
case reflect.Struct:
|
||||
// A struct type will never be false, even if
|
||||
// all of its values are the zero type.
|
||||
return false
|
||||
case reflect.Slice, reflect.Map:
|
||||
return rv.Len() == 0
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return true
|
||||
}
|
||||
// If it's a pointer type, we'll try to deref the pointer
|
||||
// and evaluate the pointer value for isFalse.
|
||||
element := rv.Elem()
|
||||
return isFalse(element.Interface())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ObjsEqual is a generic object equality check.
|
||||
// It will take two arbitrary objects and recursively determine
|
||||
// if they are equal.
|
||||
func objsEqual(left interface{}, right interface{}) bool {
|
||||
return reflect.DeepEqual(left, right)
|
||||
}
|
||||
|
||||
// SliceParam refers to a single part of a slice.
|
||||
// A slice consists of a start, a stop, and a step, similar to
|
||||
// python slices.
|
||||
type sliceParam struct {
|
||||
N int
|
||||
Specified bool
|
||||
}
|
||||
|
||||
// Slice supports [start:stop:step] style slicing that's supported in JMESPath.
|
||||
func slice(slice []interface{}, parts []sliceParam) ([]interface{}, error) {
|
||||
computed, err := computeSliceParams(len(slice), parts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start, stop, step := computed[0], computed[1], computed[2]
|
||||
result := []interface{}{}
|
||||
if step > 0 {
|
||||
for i := start; i < stop; i += step {
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
} else {
|
||||
for i := start; i > stop; i += step {
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func computeSliceParams(length int, parts []sliceParam) ([]int, error) {
|
||||
var start, stop, step int
|
||||
if !parts[2].Specified {
|
||||
step = 1
|
||||
} else if parts[2].N == 0 {
|
||||
return nil, errors.New("Invalid slice, step cannot be 0")
|
||||
} else {
|
||||
step = parts[2].N
|
||||
}
|
||||
var stepValueNegative bool
|
||||
if step < 0 {
|
||||
stepValueNegative = true
|
||||
} else {
|
||||
stepValueNegative = false
|
||||
}
|
||||
|
||||
if !parts[0].Specified {
|
||||
if stepValueNegative {
|
||||
start = length - 1
|
||||
} else {
|
||||
start = 0
|
||||
}
|
||||
} else {
|
||||
start = capSlice(length, parts[0].N, step)
|
||||
}
|
||||
|
||||
if !parts[1].Specified {
|
||||
if stepValueNegative {
|
||||
stop = -1
|
||||
} else {
|
||||
stop = length
|
||||
}
|
||||
} else {
|
||||
stop = capSlice(length, parts[1].N, step)
|
||||
}
|
||||
return []int{start, stop, step}, nil
|
||||
}
|
||||
|
||||
func capSlice(length int, actual int, step int) int {
|
||||
if actual < 0 {
|
||||
actual += length
|
||||
if actual < 0 {
|
||||
if step < 0 {
|
||||
actual = -1
|
||||
} else {
|
||||
actual = 0
|
||||
}
|
||||
}
|
||||
} else if actual >= length {
|
||||
if step < 0 {
|
||||
actual = length - 1
|
||||
} else {
|
||||
actual = length
|
||||
}
|
||||
}
|
||||
return actual
|
||||
}
|
||||
|
||||
// ToArrayNum converts an empty interface type to a slice of float64.
|
||||
// If any element in the array cannot be converted, then nil is returned
|
||||
// along with a second value of false.
|
||||
func toArrayNum(data interface{}) ([]float64, bool) {
|
||||
// Is there a better way to do this with reflect?
|
||||
if d, ok := data.([]interface{}); ok {
|
||||
result := make([]float64, len(d))
|
||||
for i, el := range d {
|
||||
item, ok := el.(float64)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
result[i] = item
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ToArrayStr converts an empty interface type to a slice of strings.
|
||||
// If any element in the array cannot be converted, then nil is returned
|
||||
// along with a second value of false. If the input data could be entirely
|
||||
// converted, then the converted data, along with a second value of true,
|
||||
// will be returned.
|
||||
func toArrayStr(data interface{}) ([]string, bool) {
|
||||
// Is there a better way to do this with reflect?
|
||||
if d, ok := data.([]interface{}); ok {
|
||||
result := make([]string, len(d))
|
||||
for i, el := range d {
|
||||
item, ok := el.(string)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
result[i] = item
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func isSliceType(v interface{}) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
return reflect.TypeOf(v).Kind() == reflect.Slice
|
||||
}
|
73
vendor/github.com/jmespath/go-jmespath/util_test.go
generated
vendored
Normal file
73
vendor/github.com/jmespath/go-jmespath/util_test.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
package jmespath
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSlicePositiveStep(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
input := make([]interface{}, 5)
|
||||
input[0] = 0
|
||||
input[1] = 1
|
||||
input[2] = 2
|
||||
input[3] = 3
|
||||
input[4] = 4
|
||||
result, err := slice(input, []sliceParam{{0, true}, {3, true}, {1, true}})
|
||||
assert.Nil(err)
|
||||
assert.Equal(input[:3], result)
|
||||
}
|
||||
|
||||
func TestIsFalseJSONTypes(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.True(isFalse(false))
|
||||
assert.True(isFalse(""))
|
||||
var empty []interface{}
|
||||
assert.True(isFalse(empty))
|
||||
m := make(map[string]interface{})
|
||||
assert.True(isFalse(m))
|
||||
assert.True(isFalse(nil))
|
||||
|
||||
}
|
||||
|
||||
func TestIsFalseWithUserDefinedStructs(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
type nilStructType struct {
|
||||
SliceOfPointers []*string
|
||||
}
|
||||
nilStruct := nilStructType{SliceOfPointers: nil}
|
||||
assert.True(isFalse(nilStruct.SliceOfPointers))
|
||||
|
||||
// A user defined struct will never be false though,
|
||||
// even if it's fields are the zero type.
|
||||
assert.False(isFalse(nilStruct))
|
||||
}
|
||||
|
||||
func TestIsFalseWithNilInterface(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
var a *int = nil
|
||||
var nilInterface interface{}
|
||||
nilInterface = a
|
||||
assert.True(isFalse(nilInterface))
|
||||
}
|
||||
|
||||
func TestIsFalseWithMapOfUserStructs(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
type foo struct {
|
||||
Bar string
|
||||
Baz string
|
||||
}
|
||||
m := make(map[int]foo)
|
||||
assert.True(isFalse(m))
|
||||
}
|
||||
|
||||
func TestObjsEqual(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.True(objsEqual("foo", "foo"))
|
||||
assert.True(objsEqual(20, 20))
|
||||
assert.True(objsEqual([]int{1, 2, 3}, []int{1, 2, 3}))
|
||||
assert.True(objsEqual(nil, nil))
|
||||
assert.True(!objsEqual(nil, "foo"))
|
||||
assert.True(objsEqual([]int{}, []int{}))
|
||||
assert.True(!objsEqual([]int{}, nil))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue