gofmt pass
This commit is contained in:
+8
-8
@@ -80,11 +80,11 @@ func (tt tokenType) String() string {
|
||||
}
|
||||
|
||||
func (t token) Int() int {
|
||||
if result, err := strconv.Atoi(t.val); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
if result, err := strconv.Atoi(t.val); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func (t token) String() string {
|
||||
@@ -291,10 +291,10 @@ func lexVoid(l *lexer) stateFn {
|
||||
return lexString
|
||||
}
|
||||
|
||||
if isSpace(next) {
|
||||
if isSpace(next) {
|
||||
l.next()
|
||||
l.ignore()
|
||||
continue
|
||||
l.ignore()
|
||||
continue
|
||||
}
|
||||
|
||||
if isAlphanumeric(next) {
|
||||
|
||||
@@ -97,4 +97,3 @@ func TestLexSpace(t *testing.T) {
|
||||
token{Position{1, 12}, tokenEOF, ""},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+124
-125
@@ -1,35 +1,34 @@
|
||||
package jpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "github.com/pelletier/go-toml"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
||||
// base match
|
||||
type matchBase struct {
|
||||
next PathFn
|
||||
next PathFn
|
||||
}
|
||||
|
||||
func (f *matchBase) SetNext(next PathFn) {
|
||||
f.next = next
|
||||
f.next = next
|
||||
}
|
||||
|
||||
// terminating functor - gathers results
|
||||
type terminatingFn struct {
|
||||
// empty
|
||||
// empty
|
||||
}
|
||||
|
||||
func newTerminatingFn() *terminatingFn {
|
||||
return &terminatingFn{}
|
||||
return &terminatingFn{}
|
||||
}
|
||||
|
||||
func (f *terminatingFn) SetNext(next PathFn) {
|
||||
// do nothing
|
||||
// do nothing
|
||||
}
|
||||
|
||||
func (f *terminatingFn) Call(node interface{}, ctx *queryContext) {
|
||||
ctx.appendResult(node)
|
||||
ctx.appendResult(node)
|
||||
}
|
||||
|
||||
// shim to ease functor writing
|
||||
@@ -39,196 +38,196 @@ func treeValue(tree *TomlTree, key string) interface{} {
|
||||
|
||||
// match single key
|
||||
type matchKeyFn struct {
|
||||
matchBase
|
||||
Name string
|
||||
matchBase
|
||||
Name string
|
||||
}
|
||||
|
||||
func newMatchKeyFn(name string) *matchKeyFn {
|
||||
return &matchKeyFn{ Name: name }
|
||||
return &matchKeyFn{Name: name}
|
||||
}
|
||||
|
||||
func (f *matchKeyFn) Call(node interface{}, ctx *queryContext) {
|
||||
if tree, ok := node.(*TomlTree); ok {
|
||||
item := treeValue(tree, f.Name)
|
||||
if item != nil {
|
||||
f.next.Call(item, ctx)
|
||||
}
|
||||
}
|
||||
if tree, ok := node.(*TomlTree); ok {
|
||||
item := treeValue(tree, f.Name)
|
||||
if item != nil {
|
||||
f.next.Call(item, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// match single index
|
||||
type matchIndexFn struct {
|
||||
matchBase
|
||||
Idx int
|
||||
matchBase
|
||||
Idx int
|
||||
}
|
||||
|
||||
func newMatchIndexFn(idx int) *matchIndexFn {
|
||||
return &matchIndexFn{ Idx: idx }
|
||||
return &matchIndexFn{Idx: idx}
|
||||
}
|
||||
|
||||
func (f *matchIndexFn) Call(node interface{}, ctx *queryContext) {
|
||||
if arr, ok := node.([]interface{}); ok {
|
||||
if f.Idx < len(arr) && f.Idx >= 0 {
|
||||
f.next.Call(arr[f.Idx], ctx)
|
||||
}
|
||||
}
|
||||
if arr, ok := node.([]interface{}); ok {
|
||||
if f.Idx < len(arr) && f.Idx >= 0 {
|
||||
f.next.Call(arr[f.Idx], ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filter by slicing
|
||||
type matchSliceFn struct {
|
||||
matchBase
|
||||
Start, End, Step int
|
||||
matchBase
|
||||
Start, End, Step int
|
||||
}
|
||||
|
||||
func newMatchSliceFn(start, end, step int) *matchSliceFn {
|
||||
return &matchSliceFn{ Start: start, End: end, Step: step }
|
||||
return &matchSliceFn{Start: start, End: end, Step: step}
|
||||
}
|
||||
|
||||
func (f *matchSliceFn) Call(node interface{}, ctx *queryContext) {
|
||||
if arr, ok := node.([]interface{}); ok {
|
||||
// adjust indexes for negative values, reverse ordering
|
||||
realStart, realEnd := f.Start, f.End
|
||||
if realStart < 0 {
|
||||
realStart = len(arr) + realStart
|
||||
}
|
||||
if realEnd < 0 {
|
||||
realEnd = len(arr) + realEnd
|
||||
}
|
||||
if realEnd < realStart {
|
||||
realEnd, realStart = realStart, realEnd // swap
|
||||
}
|
||||
// loop and gather
|
||||
for idx := realStart; idx < realEnd; idx += f.Step {
|
||||
f.next.Call(arr[idx], ctx)
|
||||
}
|
||||
}
|
||||
if arr, ok := node.([]interface{}); ok {
|
||||
// adjust indexes for negative values, reverse ordering
|
||||
realStart, realEnd := f.Start, f.End
|
||||
if realStart < 0 {
|
||||
realStart = len(arr) + realStart
|
||||
}
|
||||
if realEnd < 0 {
|
||||
realEnd = len(arr) + realEnd
|
||||
}
|
||||
if realEnd < realStart {
|
||||
realEnd, realStart = realStart, realEnd // swap
|
||||
}
|
||||
// loop and gather
|
||||
for idx := realStart; idx < realEnd; idx += f.Step {
|
||||
f.next.Call(arr[idx], ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// match anything
|
||||
type matchAnyFn struct {
|
||||
matchBase
|
||||
matchBase
|
||||
}
|
||||
|
||||
func newMatchAnyFn() *matchAnyFn {
|
||||
return &matchAnyFn{}
|
||||
return &matchAnyFn{}
|
||||
}
|
||||
|
||||
func (f *matchAnyFn) Call(node interface{}, ctx *queryContext) {
|
||||
if tree, ok := node.(*TomlTree); ok {
|
||||
for _, key := range tree.Keys() {
|
||||
item := treeValue(tree, key)
|
||||
f.next.Call(item, ctx)
|
||||
}
|
||||
}
|
||||
if tree, ok := node.(*TomlTree); ok {
|
||||
for _, key := range tree.Keys() {
|
||||
item := treeValue(tree, key)
|
||||
f.next.Call(item, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filter through union
|
||||
type matchUnionFn struct {
|
||||
Union []PathFn
|
||||
Union []PathFn
|
||||
}
|
||||
|
||||
func (f *matchUnionFn) SetNext(next PathFn) {
|
||||
for _, fn := range f.Union {
|
||||
fn.SetNext(next)
|
||||
}
|
||||
for _, fn := range f.Union {
|
||||
fn.SetNext(next)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *matchUnionFn) Call(node interface{}, ctx *queryContext) {
|
||||
for _, fn := range f.Union {
|
||||
fn.Call(node, ctx)
|
||||
}
|
||||
for _, fn := range f.Union {
|
||||
fn.Call(node, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// match every single last node in the tree
|
||||
type matchRecursiveFn struct {
|
||||
matchBase
|
||||
matchBase
|
||||
}
|
||||
|
||||
func newMatchRecursiveFn() *matchRecursiveFn{
|
||||
return &matchRecursiveFn{}
|
||||
func newMatchRecursiveFn() *matchRecursiveFn {
|
||||
return &matchRecursiveFn{}
|
||||
}
|
||||
|
||||
func (f *matchRecursiveFn) Call(node interface{}, ctx *queryContext) {
|
||||
if tree, ok := node.(*TomlTree); ok {
|
||||
var visit func(tree *TomlTree)
|
||||
visit = func(tree *TomlTree) {
|
||||
for _, key := range tree.Keys() {
|
||||
item := treeValue(tree, key)
|
||||
f.next.Call(item, ctx)
|
||||
switch node := item.(type) {
|
||||
case *TomlTree:
|
||||
visit(node)
|
||||
case []*TomlTree:
|
||||
for _, subtree := range node {
|
||||
visit(subtree)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(tree)
|
||||
}
|
||||
if tree, ok := node.(*TomlTree); ok {
|
||||
var visit func(tree *TomlTree)
|
||||
visit = func(tree *TomlTree) {
|
||||
for _, key := range tree.Keys() {
|
||||
item := treeValue(tree, key)
|
||||
f.next.Call(item, ctx)
|
||||
switch node := item.(type) {
|
||||
case *TomlTree:
|
||||
visit(node)
|
||||
case []*TomlTree:
|
||||
for _, subtree := range node {
|
||||
visit(subtree)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(tree)
|
||||
}
|
||||
}
|
||||
|
||||
// match based on an externally provided functional filter
|
||||
type matchFilterFn struct {
|
||||
matchBase
|
||||
Pos Position
|
||||
Name string
|
||||
matchBase
|
||||
Pos Position
|
||||
Name string
|
||||
}
|
||||
|
||||
func newMatchFilterFn(name string, pos Position) *matchFilterFn {
|
||||
return &matchFilterFn{ Name: name, Pos: pos }
|
||||
return &matchFilterFn{Name: name, Pos: pos}
|
||||
}
|
||||
|
||||
func (f *matchFilterFn) Call(node interface{}, ctx *queryContext) {
|
||||
fn, ok := (*ctx.filters)[f.Name]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("%s: query context does not have filter '%s'",
|
||||
f.Pos, f.Name))
|
||||
}
|
||||
switch castNode := node.(type) {
|
||||
case *TomlTree:
|
||||
for _, k := range castNode.Keys() {
|
||||
v := castNode.GetPath([]string{k})
|
||||
if fn(v) {
|
||||
f.next.Call(v, ctx)
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
for _, v := range castNode {
|
||||
if fn(v) {
|
||||
f.next.Call(v, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn, ok := (*ctx.filters)[f.Name]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("%s: query context does not have filter '%s'",
|
||||
f.Pos, f.Name))
|
||||
}
|
||||
switch castNode := node.(type) {
|
||||
case *TomlTree:
|
||||
for _, k := range castNode.Keys() {
|
||||
v := castNode.GetPath([]string{k})
|
||||
if fn(v) {
|
||||
f.next.Call(v, ctx)
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
for _, v := range castNode {
|
||||
if fn(v) {
|
||||
f.next.Call(v, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// match based using result of an externally provided functional filter
|
||||
type matchScriptFn struct {
|
||||
matchBase
|
||||
Pos Position
|
||||
Name string
|
||||
matchBase
|
||||
Pos Position
|
||||
Name string
|
||||
}
|
||||
|
||||
func newMatchScriptFn(name string, pos Position) *matchScriptFn {
|
||||
return &matchScriptFn{ Name: name, Pos: pos }
|
||||
return &matchScriptFn{Name: name, Pos: pos}
|
||||
}
|
||||
|
||||
func (f *matchScriptFn) Call(node interface{}, ctx *queryContext) {
|
||||
fn, ok := (*ctx.scripts)[f.Name]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("%s: query context does not have script '%s'",
|
||||
f.Pos, f.Name))
|
||||
}
|
||||
switch result := fn(node).(type) {
|
||||
case string:
|
||||
nextMatch := newMatchKeyFn(result)
|
||||
nextMatch.SetNext(f.next)
|
||||
nextMatch.Call(node, ctx)
|
||||
case int:
|
||||
nextMatch := newMatchIndexFn(result)
|
||||
nextMatch.SetNext(f.next)
|
||||
nextMatch.Call(node, ctx)
|
||||
//TODO: support other return types?
|
||||
}
|
||||
fn, ok := (*ctx.scripts)[f.Name]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("%s: query context does not have script '%s'",
|
||||
f.Pos, f.Name))
|
||||
}
|
||||
switch result := fn(node).(type) {
|
||||
case string:
|
||||
nextMatch := newMatchKeyFn(result)
|
||||
nextMatch.SetNext(f.next)
|
||||
nextMatch.Call(node, ctx)
|
||||
case int:
|
||||
nextMatch := newMatchIndexFn(result)
|
||||
nextMatch.SetNext(f.next)
|
||||
nextMatch.Call(node, ctx)
|
||||
//TODO: support other return types?
|
||||
}
|
||||
}
|
||||
|
||||
+96
-96
@@ -1,218 +1,218 @@
|
||||
package jpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "github.com/pelletier/go-toml"
|
||||
"fmt"
|
||||
"math"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// dump path tree to a string
|
||||
func pathString(root PathFn) string {
|
||||
result := fmt.Sprintf("%T:", root)
|
||||
switch fn := root.(type) {
|
||||
case *terminatingFn:
|
||||
result += "{}"
|
||||
case *matchKeyFn:
|
||||
result += fmt.Sprintf("{%s}", fn.Name)
|
||||
result += pathString(fn.next)
|
||||
case *matchIndexFn:
|
||||
result += fmt.Sprintf("{%d}", fn.Idx)
|
||||
result += pathString(fn.next)
|
||||
case *matchSliceFn:
|
||||
result += fmt.Sprintf("{%d:%d:%d}",
|
||||
fn.Start, fn.End, fn.Step)
|
||||
result += pathString(fn.next)
|
||||
case *matchAnyFn:
|
||||
result += "{}"
|
||||
result += pathString(fn.next)
|
||||
case *matchUnionFn:
|
||||
result += "{["
|
||||
for _, v := range fn.Union {
|
||||
result += pathString(v) + ", "
|
||||
}
|
||||
result += "]}"
|
||||
case *matchRecursiveFn:
|
||||
result += "{}"
|
||||
result += pathString(fn.next)
|
||||
case *matchFilterFn:
|
||||
result += fmt.Sprintf("{%s}", fn.Name)
|
||||
result += pathString(fn.next)
|
||||
case *matchScriptFn:
|
||||
result += fmt.Sprintf("{%s}", fn.Name)
|
||||
result += pathString(fn.next)
|
||||
}
|
||||
return result
|
||||
result := fmt.Sprintf("%T:", root)
|
||||
switch fn := root.(type) {
|
||||
case *terminatingFn:
|
||||
result += "{}"
|
||||
case *matchKeyFn:
|
||||
result += fmt.Sprintf("{%s}", fn.Name)
|
||||
result += pathString(fn.next)
|
||||
case *matchIndexFn:
|
||||
result += fmt.Sprintf("{%d}", fn.Idx)
|
||||
result += pathString(fn.next)
|
||||
case *matchSliceFn:
|
||||
result += fmt.Sprintf("{%d:%d:%d}",
|
||||
fn.Start, fn.End, fn.Step)
|
||||
result += pathString(fn.next)
|
||||
case *matchAnyFn:
|
||||
result += "{}"
|
||||
result += pathString(fn.next)
|
||||
case *matchUnionFn:
|
||||
result += "{["
|
||||
for _, v := range fn.Union {
|
||||
result += pathString(v) + ", "
|
||||
}
|
||||
result += "]}"
|
||||
case *matchRecursiveFn:
|
||||
result += "{}"
|
||||
result += pathString(fn.next)
|
||||
case *matchFilterFn:
|
||||
result += fmt.Sprintf("{%s}", fn.Name)
|
||||
result += pathString(fn.next)
|
||||
case *matchScriptFn:
|
||||
result += fmt.Sprintf("{%s}", fn.Name)
|
||||
result += pathString(fn.next)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func assertPathMatch(t *testing.T, path, ref *Query) bool {
|
||||
pathStr := pathString(path.root)
|
||||
refStr := pathString(ref.root)
|
||||
if pathStr != refStr {
|
||||
t.Errorf("paths do not match")
|
||||
t.Log("test:", pathStr)
|
||||
t.Log("ref: ", refStr)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
pathStr := pathString(path.root)
|
||||
refStr := pathString(ref.root)
|
||||
if pathStr != refStr {
|
||||
t.Errorf("paths do not match")
|
||||
t.Log("test:", pathStr)
|
||||
t.Log("ref: ", refStr)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func assertPath(t *testing.T, query string, ref *Query) {
|
||||
_, flow := lex(query)
|
||||
path := parse(flow)
|
||||
assertPathMatch(t, path, ref)
|
||||
assertPathMatch(t, path, ref)
|
||||
}
|
||||
|
||||
func buildPath(parts... PathFn) *Query {
|
||||
query := newQuery()
|
||||
for _, v := range parts {
|
||||
query.appendPath(v)
|
||||
}
|
||||
return query
|
||||
func buildPath(parts ...PathFn) *Query {
|
||||
query := newQuery()
|
||||
for _, v := range parts {
|
||||
query.appendPath(v)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func TestPathRoot(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$",
|
||||
buildPath(
|
||||
// empty
|
||||
))
|
||||
// empty
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathKey(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$.foo",
|
||||
buildPath(
|
||||
newMatchKeyFn("foo"),
|
||||
))
|
||||
newMatchKeyFn("foo"),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathBracketKey(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[foo]",
|
||||
buildPath(
|
||||
newMatchKeyFn("foo"),
|
||||
))
|
||||
newMatchKeyFn("foo"),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathBracketStringKey(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$['foo']",
|
||||
buildPath(
|
||||
newMatchKeyFn("foo"),
|
||||
))
|
||||
newMatchKeyFn("foo"),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathIndex(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123]",
|
||||
buildPath(
|
||||
newMatchIndexFn(123),
|
||||
))
|
||||
newMatchIndexFn(123),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathSliceStart(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123:]",
|
||||
buildPath(
|
||||
newMatchSliceFn(123, math.MaxInt64, 1),
|
||||
))
|
||||
newMatchSliceFn(123, math.MaxInt64, 1),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathSliceStartEnd(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123:456]",
|
||||
buildPath(
|
||||
newMatchSliceFn(123, 456, 1),
|
||||
))
|
||||
newMatchSliceFn(123, 456, 1),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathSliceStartEndColon(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123:456:]",
|
||||
buildPath(
|
||||
newMatchSliceFn(123, 456, 1),
|
||||
))
|
||||
newMatchSliceFn(123, 456, 1),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathSliceStartStep(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123::7]",
|
||||
buildPath(
|
||||
newMatchSliceFn(123, math.MaxInt64, 7),
|
||||
))
|
||||
newMatchSliceFn(123, math.MaxInt64, 7),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathSliceEndStep(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[:456:7]",
|
||||
buildPath(
|
||||
newMatchSliceFn(0, 456, 7),
|
||||
))
|
||||
newMatchSliceFn(0, 456, 7),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathSliceStep(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[::7]",
|
||||
buildPath(
|
||||
newMatchSliceFn(0, math.MaxInt64, 7),
|
||||
))
|
||||
newMatchSliceFn(0, math.MaxInt64, 7),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathSliceAll(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[123:456:7]",
|
||||
buildPath(
|
||||
newMatchSliceFn(123, 456, 7),
|
||||
))
|
||||
newMatchSliceFn(123, 456, 7),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathAny(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$.*",
|
||||
buildPath(
|
||||
newMatchAnyFn(),
|
||||
))
|
||||
newMatchAnyFn(),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathUnion(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[foo, bar, baz]",
|
||||
buildPath(
|
||||
&matchUnionFn{ []PathFn {
|
||||
newMatchKeyFn("foo"),
|
||||
newMatchKeyFn("bar"),
|
||||
newMatchKeyFn("baz"),
|
||||
}},
|
||||
))
|
||||
&matchUnionFn{[]PathFn{
|
||||
newMatchKeyFn("foo"),
|
||||
newMatchKeyFn("bar"),
|
||||
newMatchKeyFn("baz"),
|
||||
}},
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathRecurse(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$..*",
|
||||
buildPath(
|
||||
newMatchRecursiveFn(),
|
||||
))
|
||||
newMatchRecursiveFn(),
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathFilterExpr(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[?('foo'),?(bar)]",
|
||||
buildPath(
|
||||
&matchUnionFn{ []PathFn {
|
||||
newMatchFilterFn("foo", Position{}),
|
||||
newMatchFilterFn("bar", Position{}),
|
||||
}},
|
||||
))
|
||||
&matchUnionFn{[]PathFn{
|
||||
newMatchFilterFn("foo", Position{}),
|
||||
newMatchFilterFn("bar", Position{}),
|
||||
}},
|
||||
))
|
||||
}
|
||||
|
||||
func TestPathScriptExpr(t *testing.T) {
|
||||
assertPath(t,
|
||||
"$[('foo'),(bar)]",
|
||||
buildPath(
|
||||
&matchUnionFn{ []PathFn {
|
||||
newMatchScriptFn("foo", Position{}),
|
||||
newMatchScriptFn("bar", Position{}),
|
||||
}},
|
||||
))
|
||||
&matchUnionFn{[]PathFn{
|
||||
newMatchScriptFn("foo", Position{}),
|
||||
newMatchScriptFn("bar", Position{}),
|
||||
}},
|
||||
))
|
||||
}
|
||||
|
||||
+100
-100
@@ -16,7 +16,7 @@ type parser struct {
|
||||
flow chan token
|
||||
tokensBuffer []token
|
||||
path *Query
|
||||
union []PathFn
|
||||
union []PathFn
|
||||
}
|
||||
|
||||
type parserStateFn func(*parser) parserStateFn
|
||||
@@ -49,25 +49,25 @@ func (p *parser) peek() *token {
|
||||
return &tok
|
||||
}
|
||||
|
||||
func (p *parser) lookahead(types... tokenType) bool {
|
||||
result := true
|
||||
buffer := []token{}
|
||||
func (p *parser) lookahead(types ...tokenType) bool {
|
||||
result := true
|
||||
buffer := []token{}
|
||||
|
||||
for _, typ := range types {
|
||||
tok := p.getToken()
|
||||
if tok == nil {
|
||||
result = false
|
||||
break
|
||||
}
|
||||
buffer = append(buffer, *tok)
|
||||
if tok.typ != typ {
|
||||
result = false
|
||||
break
|
||||
}
|
||||
}
|
||||
// add the tokens back to the buffer, and return
|
||||
p.tokensBuffer = append(p.tokensBuffer, buffer...)
|
||||
return result
|
||||
for _, typ := range types {
|
||||
tok := p.getToken()
|
||||
if tok == nil {
|
||||
result = false
|
||||
break
|
||||
}
|
||||
buffer = append(buffer, *tok)
|
||||
if tok.typ != typ {
|
||||
result = false
|
||||
break
|
||||
}
|
||||
}
|
||||
// add the tokens back to the buffer, and return
|
||||
p.tokensBuffer = append(p.tokensBuffer, buffer...)
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *parser) getToken() *token {
|
||||
@@ -102,31 +102,31 @@ func parseMatchExpr(p *parser) parserStateFn {
|
||||
tok := p.getToken()
|
||||
switch tok.typ {
|
||||
case tokenDotDot:
|
||||
p.path.appendPath(&matchRecursiveFn{})
|
||||
// nested parse for '..'
|
||||
tok := p.getToken()
|
||||
switch tok.typ {
|
||||
case tokenKey:
|
||||
p.path.appendPath(newMatchKeyFn(tok.val))
|
||||
return parseMatchExpr
|
||||
case tokenLBracket:
|
||||
return parseBracketExpr
|
||||
case tokenStar:
|
||||
// do nothing - the recursive predicate is enough
|
||||
return parseMatchExpr
|
||||
}
|
||||
p.path.appendPath(&matchRecursiveFn{})
|
||||
// nested parse for '..'
|
||||
tok := p.getToken()
|
||||
switch tok.typ {
|
||||
case tokenKey:
|
||||
p.path.appendPath(newMatchKeyFn(tok.val))
|
||||
return parseMatchExpr
|
||||
case tokenLBracket:
|
||||
return parseBracketExpr
|
||||
case tokenStar:
|
||||
// do nothing - the recursive predicate is enough
|
||||
return parseMatchExpr
|
||||
}
|
||||
|
||||
case tokenDot:
|
||||
// nested parse for '.'
|
||||
tok := p.getToken()
|
||||
switch tok.typ {
|
||||
case tokenKey:
|
||||
p.path.appendPath(newMatchKeyFn(tok.val))
|
||||
return parseMatchExpr
|
||||
case tokenStar:
|
||||
p.path.appendPath(&matchAnyFn{})
|
||||
return parseMatchExpr
|
||||
}
|
||||
// nested parse for '.'
|
||||
tok := p.getToken()
|
||||
switch tok.typ {
|
||||
case tokenKey:
|
||||
p.path.appendPath(newMatchKeyFn(tok.val))
|
||||
return parseMatchExpr
|
||||
case tokenStar:
|
||||
p.path.appendPath(&matchAnyFn{})
|
||||
return parseMatchExpr
|
||||
}
|
||||
|
||||
case tokenLBracket:
|
||||
return parseBracketExpr
|
||||
@@ -139,38 +139,38 @@ func parseMatchExpr(p *parser) parserStateFn {
|
||||
}
|
||||
|
||||
func parseBracketExpr(p *parser) parserStateFn {
|
||||
if p.lookahead(tokenInteger, tokenColon) {
|
||||
return parseSliceExpr
|
||||
}
|
||||
if p.peek().typ == tokenColon {
|
||||
return parseSliceExpr
|
||||
}
|
||||
if p.lookahead(tokenInteger, tokenColon) {
|
||||
return parseSliceExpr
|
||||
}
|
||||
if p.peek().typ == tokenColon {
|
||||
return parseSliceExpr
|
||||
}
|
||||
return parseUnionExpr
|
||||
}
|
||||
|
||||
func parseUnionExpr(p *parser) parserStateFn {
|
||||
var tok *token
|
||||
var tok *token
|
||||
|
||||
// this state can be traversed after some sub-expressions
|
||||
// so be careful when setting up state in the parser
|
||||
// this state can be traversed after some sub-expressions
|
||||
// so be careful when setting up state in the parser
|
||||
if p.union == nil {
|
||||
p.union = []PathFn{}
|
||||
}
|
||||
p.union = []PathFn{}
|
||||
}
|
||||
|
||||
loop: // labeled loop for easy breaking
|
||||
for {
|
||||
if len(p.union) > 0 {
|
||||
// parse delimiter or terminator
|
||||
tok = p.getToken()
|
||||
switch tok.typ {
|
||||
case tokenComma:
|
||||
// do nothing
|
||||
case tokenRBracket:
|
||||
break loop
|
||||
default:
|
||||
p.raiseError(tok, "expected ',' or ']', not '%s'", tok.val)
|
||||
}
|
||||
}
|
||||
for {
|
||||
if len(p.union) > 0 {
|
||||
// parse delimiter or terminator
|
||||
tok = p.getToken()
|
||||
switch tok.typ {
|
||||
case tokenComma:
|
||||
// do nothing
|
||||
case tokenRBracket:
|
||||
break loop
|
||||
default:
|
||||
p.raiseError(tok, "expected ',' or ']', not '%s'", tok.val)
|
||||
}
|
||||
}
|
||||
|
||||
// parse sub expression
|
||||
tok = p.getToken()
|
||||
@@ -190,14 +190,14 @@ loop: // labeled loop for easy breaking
|
||||
}
|
||||
}
|
||||
|
||||
// if there is only one sub-expression, use that instead
|
||||
if len(p.union) == 1 {
|
||||
p.path.appendPath(p.union[0])
|
||||
}else {
|
||||
p.path.appendPath(&matchUnionFn{p.union})
|
||||
}
|
||||
// if there is only one sub-expression, use that instead
|
||||
if len(p.union) == 1 {
|
||||
p.path.appendPath(p.union[0])
|
||||
} else {
|
||||
p.path.appendPath(&matchUnionFn{p.union})
|
||||
}
|
||||
|
||||
p.union = nil // clear out state
|
||||
p.union = nil // clear out state
|
||||
return parseMatchExpr
|
||||
}
|
||||
|
||||
@@ -221,11 +221,11 @@ func parseSliceExpr(p *parser) parserStateFn {
|
||||
end = tok.Int()
|
||||
tok = p.getToken()
|
||||
}
|
||||
if tok.typ == tokenRBracket {
|
||||
p.path.appendPath(newMatchSliceFn(start, end, step))
|
||||
return parseMatchExpr
|
||||
}
|
||||
if tok.typ != tokenColon {
|
||||
if tok.typ == tokenRBracket {
|
||||
p.path.appendPath(newMatchSliceFn(start, end, step))
|
||||
return parseMatchExpr
|
||||
}
|
||||
if tok.typ != tokenColon {
|
||||
p.raiseError(tok, "expected ']' or ':'")
|
||||
}
|
||||
|
||||
@@ -247,33 +247,33 @@ func parseSliceExpr(p *parser) parserStateFn {
|
||||
}
|
||||
|
||||
func parseFilterExpr(p *parser) parserStateFn {
|
||||
tok := p.getToken()
|
||||
if tok.typ != tokenLParen {
|
||||
p.raiseError(tok, "expected left-parenthesis for filter expression")
|
||||
}
|
||||
tok = p.getToken()
|
||||
if tok.typ != tokenKey && tok.typ != tokenString {
|
||||
p.raiseError(tok, "expected key or string for filter funciton name")
|
||||
}
|
||||
name := tok.val
|
||||
tok = p.getToken()
|
||||
if tok.typ != tokenRParen {
|
||||
p.raiseError(tok, "expected right-parenthesis for filter expression")
|
||||
}
|
||||
tok := p.getToken()
|
||||
if tok.typ != tokenLParen {
|
||||
p.raiseError(tok, "expected left-parenthesis for filter expression")
|
||||
}
|
||||
tok = p.getToken()
|
||||
if tok.typ != tokenKey && tok.typ != tokenString {
|
||||
p.raiseError(tok, "expected key or string for filter funciton name")
|
||||
}
|
||||
name := tok.val
|
||||
tok = p.getToken()
|
||||
if tok.typ != tokenRParen {
|
||||
p.raiseError(tok, "expected right-parenthesis for filter expression")
|
||||
}
|
||||
p.union = append(p.union, newMatchFilterFn(name, tok.Position))
|
||||
return parseUnionExpr
|
||||
}
|
||||
|
||||
func parseScriptExpr(p *parser) parserStateFn {
|
||||
tok := p.getToken()
|
||||
if tok.typ != tokenKey && tok.typ != tokenString {
|
||||
p.raiseError(tok, "expected key or string for script funciton name")
|
||||
}
|
||||
name := tok.val
|
||||
tok = p.getToken()
|
||||
if tok.typ != tokenRParen {
|
||||
p.raiseError(tok, "expected right-parenthesis for script expression")
|
||||
}
|
||||
tok := p.getToken()
|
||||
if tok.typ != tokenKey && tok.typ != tokenString {
|
||||
p.raiseError(tok, "expected key or string for script funciton name")
|
||||
}
|
||||
name := tok.val
|
||||
tok = p.getToken()
|
||||
if tok.typ != tokenRParen {
|
||||
p.raiseError(tok, "expected right-parenthesis for script expression")
|
||||
}
|
||||
p.union = append(p.union, newMatchScriptFn(name, tok.Position))
|
||||
return parseUnionExpr
|
||||
}
|
||||
|
||||
+148
-148
@@ -12,8 +12,8 @@ func assertQuery(t *testing.T, toml, query string, ref []interface{}) {
|
||||
t.Errorf("Non-nil toml parse error: %v", err)
|
||||
return
|
||||
}
|
||||
results := Compile(query).Execute(tree)
|
||||
assertValue(t, results, ref, "((" + query + ")) -> ")
|
||||
results := Compile(query).Execute(tree)
|
||||
assertValue(t, results, ref, "(("+query+")) -> ")
|
||||
}
|
||||
|
||||
func assertValue(t *testing.T, result, ref interface{}, location string) {
|
||||
@@ -23,14 +23,14 @@ func assertValue(t *testing.T, result, ref interface{}, location string) {
|
||||
t.Errorf("{%s} result value not of type %T: %T",
|
||||
location, node, resultNode)
|
||||
} else {
|
||||
if len(node) != len(resultNode) {
|
||||
t.Errorf("{%s} lengths do not match: %v vs %v",
|
||||
location, node, resultNode)
|
||||
} else {
|
||||
for i, v := range node {
|
||||
assertValue(t, resultNode[i], v, fmt.Sprintf("%s[%d]", location, i))
|
||||
}
|
||||
}
|
||||
if len(node) != len(resultNode) {
|
||||
t.Errorf("{%s} lengths do not match: %v vs %v",
|
||||
location, node, resultNode)
|
||||
} else {
|
||||
for i, v := range node {
|
||||
assertValue(t, resultNode[i], v, fmt.Sprintf("%s[%d]", location, i))
|
||||
}
|
||||
}
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if resultNode, ok := result.(*TomlTree); !ok {
|
||||
@@ -79,186 +79,186 @@ func TestQueryRoot(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestQueryKey(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo]\na = 42",
|
||||
"$.foo.a",
|
||||
assertQuery(t,
|
||||
"[foo]\na = 42",
|
||||
"$.foo.a",
|
||||
[]interface{}{
|
||||
int64(42),
|
||||
})
|
||||
int64(42),
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryKeyString(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo]\na = 42",
|
||||
"$.foo['a']",
|
||||
assertQuery(t,
|
||||
"[foo]\na = 42",
|
||||
"$.foo['a']",
|
||||
[]interface{}{
|
||||
int64(42),
|
||||
})
|
||||
int64(42),
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryIndex(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo]\na = [1,2,3,4,5,6,7,8,9,0]",
|
||||
"$.foo.a[0]",
|
||||
assertQuery(t,
|
||||
"[foo]\na = [1,2,3,4,5,6,7,8,9,0]",
|
||||
"$.foo.a[0]",
|
||||
[]interface{}{
|
||||
int64(1),
|
||||
})
|
||||
int64(1),
|
||||
})
|
||||
}
|
||||
|
||||
func TestQuerySliceRange(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo]\na = [1,2,3,4,5,6,7,8,9,0]",
|
||||
"$.foo.a[0:5]",
|
||||
assertQuery(t,
|
||||
"[foo]\na = [1,2,3,4,5,6,7,8,9,0]",
|
||||
"$.foo.a[0:5]",
|
||||
[]interface{}{
|
||||
int64(1),
|
||||
int64(2),
|
||||
int64(3),
|
||||
int64(4),
|
||||
int64(5),
|
||||
})
|
||||
int64(1),
|
||||
int64(2),
|
||||
int64(3),
|
||||
int64(4),
|
||||
int64(5),
|
||||
})
|
||||
}
|
||||
|
||||
func TestQuerySliceStep(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo]\na = [1,2,3,4,5,6,7,8,9,0]",
|
||||
"$.foo.a[0:5:2]",
|
||||
assertQuery(t,
|
||||
"[foo]\na = [1,2,3,4,5,6,7,8,9,0]",
|
||||
"$.foo.a[0:5:2]",
|
||||
[]interface{}{
|
||||
int64(1),
|
||||
int64(3),
|
||||
int64(5),
|
||||
})
|
||||
int64(1),
|
||||
int64(3),
|
||||
int64(5),
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryAny(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo.bar]\na=1\nb=2\n[foo.baz]\na=3\nb=4",
|
||||
"$.foo.*",
|
||||
assertQuery(t,
|
||||
"[foo.bar]\na=1\nb=2\n[foo.baz]\na=3\nb=4",
|
||||
"$.foo.*",
|
||||
[]interface{}{
|
||||
map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
})
|
||||
map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
})
|
||||
}
|
||||
func TestQueryUnionSimple(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
|
||||
"$.*[bar,foo]",
|
||||
assertQuery(t,
|
||||
"[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
|
||||
"$.*[bar,foo]",
|
||||
[]interface{}{
|
||||
map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(5),
|
||||
"b": int64(6),
|
||||
},
|
||||
})
|
||||
map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(5),
|
||||
"b": int64(6),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryRecursionAll(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
|
||||
"$..*",
|
||||
assertQuery(t,
|
||||
"[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
|
||||
"$..*",
|
||||
[]interface{}{
|
||||
map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
int64(1),
|
||||
int64(2),
|
||||
map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
int64(3),
|
||||
int64(4),
|
||||
map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"a": int64(5),
|
||||
"b": int64(6),
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(5),
|
||||
"b": int64(6),
|
||||
},
|
||||
int64(5),
|
||||
int64(6),
|
||||
})
|
||||
map[string]interface{}{
|
||||
"bar": map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
int64(1),
|
||||
int64(2),
|
||||
map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
int64(3),
|
||||
int64(4),
|
||||
map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"a": int64(5),
|
||||
"b": int64(6),
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(5),
|
||||
"b": int64(6),
|
||||
},
|
||||
int64(5),
|
||||
int64(6),
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryRecursionUnionSimple(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
|
||||
"$..['foo','bar']",
|
||||
assertQuery(t,
|
||||
"[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6",
|
||||
"$..['foo','bar']",
|
||||
[]interface{}{
|
||||
map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(5),
|
||||
"b": int64(6),
|
||||
},
|
||||
})
|
||||
map[string]interface{}{
|
||||
"a": int64(1),
|
||||
"b": int64(2),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(3),
|
||||
"b": int64(4),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"a": int64(5),
|
||||
"b": int64(6),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryScriptFnLast(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
|
||||
"$.foo.a[(last)]",
|
||||
assertQuery(t,
|
||||
"[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
|
||||
"$.foo.a[(last)]",
|
||||
[]interface{}{
|
||||
int64(9),
|
||||
})
|
||||
int64(9),
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryFilterFnOdd(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
|
||||
"$.foo.a[?(odd)]",
|
||||
assertQuery(t,
|
||||
"[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
|
||||
"$.foo.a[?(odd)]",
|
||||
[]interface{}{
|
||||
int64(1),
|
||||
int64(3),
|
||||
int64(5),
|
||||
int64(7),
|
||||
int64(9),
|
||||
})
|
||||
int64(1),
|
||||
int64(3),
|
||||
int64(5),
|
||||
int64(7),
|
||||
int64(9),
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueryFilterFnEven(t *testing.T) {
|
||||
assertQuery(t,
|
||||
"[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
|
||||
"$.foo.a[?(even)]",
|
||||
assertQuery(t,
|
||||
"[foo]\na = [0,1,2,3,4,5,6,7,8,9]",
|
||||
"$.foo.a[?(even)]",
|
||||
[]interface{}{
|
||||
int64(0),
|
||||
int64(2),
|
||||
int64(4),
|
||||
int64(6),
|
||||
int64(8),
|
||||
})
|
||||
int64(0),
|
||||
int64(2),
|
||||
int64(4),
|
||||
int64(6),
|
||||
int64(8),
|
||||
})
|
||||
}
|
||||
|
||||
+72
-73
@@ -1,7 +1,7 @@
|
||||
package jpath
|
||||
|
||||
import (
|
||||
_ "github.com/pelletier/go-toml"
|
||||
_ "github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
type nodeFilterFn func(node interface{}) bool
|
||||
@@ -9,109 +9,108 @@ type nodeFn func(node interface{}) interface{}
|
||||
|
||||
// runtime context for executing query paths
|
||||
type queryContext struct {
|
||||
filters *map[string]nodeFilterFn
|
||||
scripts *map[string]nodeFn
|
||||
results []interface{}
|
||||
filters *map[string]nodeFilterFn
|
||||
scripts *map[string]nodeFn
|
||||
results []interface{}
|
||||
}
|
||||
|
||||
func (c *queryContext) appendResult(value interface{}) {
|
||||
c.results = append(c.results, value)
|
||||
c.results = append(c.results, value)
|
||||
}
|
||||
|
||||
// generic path functor interface
|
||||
type PathFn interface {
|
||||
SetNext(next PathFn)
|
||||
Call(node interface{}, ctx *queryContext)
|
||||
SetNext(next PathFn)
|
||||
Call(node interface{}, ctx *queryContext)
|
||||
}
|
||||
|
||||
// encapsulates a query functor chain and script callbacks
|
||||
type Query struct {
|
||||
root PathFn
|
||||
tail PathFn
|
||||
filters *map[string]nodeFilterFn
|
||||
scripts *map[string]nodeFn
|
||||
root PathFn
|
||||
tail PathFn
|
||||
filters *map[string]nodeFilterFn
|
||||
scripts *map[string]nodeFn
|
||||
}
|
||||
|
||||
func newQuery() *Query {
|
||||
return &Query {
|
||||
root: nil,
|
||||
tail: nil,
|
||||
filters: &defaultFilterFunctions,
|
||||
scripts: &defaultScriptFunctions,
|
||||
}
|
||||
return &Query{
|
||||
root: nil,
|
||||
tail: nil,
|
||||
filters: &defaultFilterFunctions,
|
||||
scripts: &defaultScriptFunctions,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) appendPath(next PathFn) {
|
||||
if q.root == nil {
|
||||
q.root = next
|
||||
} else {
|
||||
q.tail.SetNext(next)
|
||||
}
|
||||
q.tail = next
|
||||
next.SetNext(newTerminatingFn()) // init the next functor
|
||||
if q.root == nil {
|
||||
q.root = next
|
||||
} else {
|
||||
q.tail.SetNext(next)
|
||||
}
|
||||
q.tail = next
|
||||
next.SetNext(newTerminatingFn()) // init the next functor
|
||||
}
|
||||
|
||||
func Compile(path string) *Query {
|
||||
_, flow := lex(path)
|
||||
return parse(flow)
|
||||
_, flow := lex(path)
|
||||
return parse(flow)
|
||||
}
|
||||
|
||||
func (q *Query) Execute(node interface{}) interface{} {
|
||||
if q.root == nil {
|
||||
return []interface{}{node} // identity query for no predicates
|
||||
}
|
||||
ctx := &queryContext {
|
||||
filters: q.filters,
|
||||
scripts: q.scripts,
|
||||
results: []interface{}{},
|
||||
}
|
||||
q.root.Call(node, ctx)
|
||||
return ctx.results
|
||||
if q.root == nil {
|
||||
return []interface{}{node} // identity query for no predicates
|
||||
}
|
||||
ctx := &queryContext{
|
||||
filters: q.filters,
|
||||
scripts: q.scripts,
|
||||
results: []interface{}{},
|
||||
}
|
||||
q.root.Call(node, ctx)
|
||||
return ctx.results
|
||||
}
|
||||
|
||||
func (q *Query) SetFilter(name string, fn nodeFilterFn) {
|
||||
if q.filters == &defaultFilterFunctions {
|
||||
// clone the static table
|
||||
q.filters = &map[string]nodeFilterFn{}
|
||||
for k, v := range defaultFilterFunctions {
|
||||
(*q.filters)[k] = v
|
||||
}
|
||||
}
|
||||
(*q.filters)[name] = fn
|
||||
if q.filters == &defaultFilterFunctions {
|
||||
// clone the static table
|
||||
q.filters = &map[string]nodeFilterFn{}
|
||||
for k, v := range defaultFilterFunctions {
|
||||
(*q.filters)[k] = v
|
||||
}
|
||||
}
|
||||
(*q.filters)[name] = fn
|
||||
}
|
||||
|
||||
func (q *Query) SetScript(name string, fn nodeFn) {
|
||||
if q.scripts == &defaultScriptFunctions {
|
||||
// clone the static table
|
||||
q.scripts = &map[string]nodeFn{}
|
||||
for k, v := range defaultScriptFunctions {
|
||||
(*q.scripts)[k] = v
|
||||
}
|
||||
}
|
||||
(*q.scripts)[name] = fn
|
||||
if q.scripts == &defaultScriptFunctions {
|
||||
// clone the static table
|
||||
q.scripts = &map[string]nodeFn{}
|
||||
for k, v := range defaultScriptFunctions {
|
||||
(*q.scripts)[k] = v
|
||||
}
|
||||
}
|
||||
(*q.scripts)[name] = fn
|
||||
}
|
||||
|
||||
var defaultFilterFunctions = map[string]nodeFilterFn {
|
||||
"odd": func(node interface{}) bool {
|
||||
if ii, ok := node.(int64); ok {
|
||||
return (ii & 1) == 1
|
||||
}
|
||||
return false
|
||||
},
|
||||
"even": func(node interface{}) bool {
|
||||
if ii, ok := node.(int64); ok {
|
||||
return (ii & 1) == 0
|
||||
}
|
||||
return false
|
||||
},
|
||||
var defaultFilterFunctions = map[string]nodeFilterFn{
|
||||
"odd": func(node interface{}) bool {
|
||||
if ii, ok := node.(int64); ok {
|
||||
return (ii & 1) == 1
|
||||
}
|
||||
return false
|
||||
},
|
||||
"even": func(node interface{}) bool {
|
||||
if ii, ok := node.(int64); ok {
|
||||
return (ii & 1) == 0
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
|
||||
var defaultScriptFunctions = map[string]nodeFn {
|
||||
"last": func(node interface{}) interface{} {
|
||||
if arr, ok := node.([]interface{}); ok {
|
||||
return len(arr)-1
|
||||
}
|
||||
return nil
|
||||
},
|
||||
var defaultScriptFunctions = map[string]nodeFn{
|
||||
"last": func(node interface{}) interface{} {
|
||||
if arr, ok := node.([]interface{}); ok {
|
||||
return len(arr) - 1
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user