Refactored match to use function chaining
This commit is contained in:
+119
-38
@@ -4,14 +4,84 @@ import (
|
|||||||
. "github.com/pelletier/go-toml"
|
. "github.com/pelletier/go-toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PathFn interface{
|
// result set for storage of results
|
||||||
Call(context interface{}, next QueryPath)
|
type pathResult struct {
|
||||||
|
Values []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryPath []PathFn
|
func newPathResult() *pathResult {
|
||||||
|
return &pathResult {
|
||||||
|
Values: []interface{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (path QueryPath) Fn(context interface{}) {
|
func (r *pathResult) Append(value interface{}) {
|
||||||
path[0].Call(context, path[1:])
|
r.Values = append(r.Values, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generic path functor interface
|
||||||
|
type PathFn interface{
|
||||||
|
SetNext(next PathFn)
|
||||||
|
Call(context interface{}, results *pathResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains a functor chain
|
||||||
|
type QueryPath struct {
|
||||||
|
root PathFn
|
||||||
|
tail PathFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func newQueryPath() *QueryPath {
|
||||||
|
return &QueryPath {
|
||||||
|
root: nil,
|
||||||
|
tail: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (path *QueryPath) Append(next PathFn) {
|
||||||
|
if path.root == nil {
|
||||||
|
path.root = next
|
||||||
|
} else {
|
||||||
|
path.tail.SetNext(next)
|
||||||
|
}
|
||||||
|
path.tail = next
|
||||||
|
next.SetNext(newTerminatingFn()) // init the next functor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (path *QueryPath) Call(context interface{}) []interface{} {
|
||||||
|
results := newPathResult()
|
||||||
|
if path.root == nil {
|
||||||
|
results.Append(context) // identity query for no predicates
|
||||||
|
} else {
|
||||||
|
path.root.Call(context, results)
|
||||||
|
}
|
||||||
|
return results.Values
|
||||||
|
}
|
||||||
|
|
||||||
|
// base match
|
||||||
|
type matchBase struct {
|
||||||
|
next PathFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *matchBase) SetNext(next PathFn) {
|
||||||
|
f.next = next
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminating functor - gathers results
|
||||||
|
type terminatingFn struct {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTerminatingFn() *terminatingFn {
|
||||||
|
return &terminatingFn{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *terminatingFn) SetNext(next PathFn) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *terminatingFn) Call(context interface{}, results *pathResult) {
|
||||||
|
results.Append(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shim to ease functor writing
|
// shim to ease functor writing
|
||||||
@@ -21,37 +91,52 @@ func treeValue(tree *TomlTree, key string) interface{} {
|
|||||||
|
|
||||||
// match single key
|
// match single key
|
||||||
type matchKeyFn struct {
|
type matchKeyFn struct {
|
||||||
|
matchBase
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *matchKeyFn) Call(context interface{}, next QueryPath) {
|
func newMatchKeyFn(name string) *matchKeyFn {
|
||||||
|
return &matchKeyFn{ Name: name }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *matchKeyFn) Call(context interface{}, results *pathResult) {
|
||||||
if tree, ok := context.(*TomlTree); ok {
|
if tree, ok := context.(*TomlTree); ok {
|
||||||
item := treeValue(tree, f.Name)
|
item := treeValue(tree, f.Name)
|
||||||
if item != nil {
|
if item != nil {
|
||||||
next.Fn(item)
|
f.next.Call(item, results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// match single index
|
// match single index
|
||||||
type matchIndexFn struct {
|
type matchIndexFn struct {
|
||||||
|
matchBase
|
||||||
Idx int
|
Idx int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *matchIndexFn) Call(context interface{}, next QueryPath) {
|
func newMatchIndexFn(idx int) *matchIndexFn {
|
||||||
|
return &matchIndexFn{ Idx: idx }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *matchIndexFn) Call(context interface{}, results *pathResult) {
|
||||||
if arr, ok := context.([]interface{}); ok {
|
if arr, ok := context.([]interface{}); ok {
|
||||||
if f.Idx < len(arr) && f.Idx >= 0 {
|
if f.Idx < len(arr) && f.Idx >= 0 {
|
||||||
next.Fn(arr[f.Idx])
|
f.next.Call(arr[f.Idx], results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter by slicing
|
// filter by slicing
|
||||||
type matchSliceFn struct {
|
type matchSliceFn struct {
|
||||||
|
matchBase
|
||||||
Start, End, Step int
|
Start, End, Step int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *matchSliceFn) Call(context interface{}, next QueryPath) {
|
func newMatchSliceFn(start, end, step int) *matchSliceFn {
|
||||||
|
return &matchSliceFn{ Start: start, End: end, Step: step }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *matchSliceFn) Call(context interface{}, results *pathResult) {
|
||||||
if arr, ok := context.([]interface{}); ok {
|
if arr, ok := context.([]interface{}); ok {
|
||||||
// adjust indexes for negative values, reverse ordering
|
// adjust indexes for negative values, reverse ordering
|
||||||
realStart, realEnd := f.Start, f.End
|
realStart, realEnd := f.Start, f.End
|
||||||
@@ -66,48 +151,63 @@ func (f *matchSliceFn) Call(context interface{}, next QueryPath) {
|
|||||||
}
|
}
|
||||||
// loop and gather
|
// loop and gather
|
||||||
for idx := realStart; idx < realEnd; idx += f.Step {
|
for idx := realStart; idx < realEnd; idx += f.Step {
|
||||||
next.Fn(arr[idx])
|
f.next.Call(arr[idx], results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// match anything
|
// match anything
|
||||||
type matchAnyFn struct {
|
type matchAnyFn struct {
|
||||||
|
matchBase
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *matchAnyFn) Call(context interface{}, next QueryPath) {
|
func newMatchAnyFn() *matchAnyFn {
|
||||||
|
return &matchAnyFn{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *matchAnyFn) Call(context interface{}, results *pathResult) {
|
||||||
if tree, ok := context.(*TomlTree); ok {
|
if tree, ok := context.(*TomlTree); ok {
|
||||||
for _, key := range tree.Keys() {
|
for _, key := range tree.Keys() {
|
||||||
item := treeValue(tree, key)
|
item := treeValue(tree, key)
|
||||||
next.Fn(item)
|
f.next.Call(item, results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter through union
|
// filter through union
|
||||||
type matchUnionFn struct {
|
type matchUnionFn struct {
|
||||||
Union QueryPath
|
Union []PathFn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *matchUnionFn) Call(context interface{}, next QueryPath) {
|
func (f *matchUnionFn) SetNext(next PathFn) {
|
||||||
for _, fn := range f.Union {
|
for _, fn := range f.Union {
|
||||||
fn.Call(context, next)
|
fn.SetNext(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *matchUnionFn) Call(context interface{}, results *pathResult) {
|
||||||
|
for _, fn := range f.Union {
|
||||||
|
fn.Call(context, results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// match every single last node in the tree
|
// match every single last node in the tree
|
||||||
type matchRecursiveFn struct {
|
type matchRecursiveFn struct {
|
||||||
// empty
|
matchBase
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *matchRecursiveFn) Call(context interface{}, next QueryPath) {
|
func newMatchRecursiveFn() *matchRecursiveFn{
|
||||||
|
return &matchRecursiveFn{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *matchRecursiveFn) Call(context interface{}, results *pathResult) {
|
||||||
if tree, ok := context.(*TomlTree); ok {
|
if tree, ok := context.(*TomlTree); ok {
|
||||||
var visit func(tree *TomlTree)
|
var visit func(tree *TomlTree)
|
||||||
visit = func(tree *TomlTree) {
|
visit = func(tree *TomlTree) {
|
||||||
for _, key := range tree.Keys() {
|
for _, key := range tree.Keys() {
|
||||||
item := treeValue(tree, key)
|
item := treeValue(tree, key)
|
||||||
next.Fn(item)
|
f.next.Call(item, results)
|
||||||
switch node := item.(type) {
|
switch node := item.(type) {
|
||||||
case *TomlTree:
|
case *TomlTree:
|
||||||
visit(node)
|
visit(node)
|
||||||
@@ -121,22 +221,3 @@ func (f *matchRecursiveFn) Call(context interface{}, next QueryPath) {
|
|||||||
visit(tree)
|
visit(tree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// terminating expression
|
|
||||||
type matchEndFn struct {
|
|
||||||
Result []interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *matchEndFn) Call(context interface{}, next QueryPath) {
|
|
||||||
f.Result = append(f.Result, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
func processPath(path QueryPath, context interface{}) []interface{} {
|
|
||||||
// terminate the path with a collection funciton
|
|
||||||
endFn := &matchEndFn{ []interface{}{} }
|
|
||||||
newPath := append(path, endFn)
|
|
||||||
|
|
||||||
// execute the path
|
|
||||||
newPath.Fn(context)
|
|
||||||
return endFn.Result
|
|
||||||
}
|
|
||||||
|
|||||||
+91
-88
@@ -6,181 +6,184 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pathString(path QueryPath) string {
|
// dump path tree to a string
|
||||||
result := "["
|
func pathString(root PathFn) string {
|
||||||
for _, v := range path {
|
result := fmt.Sprintf("%T:")
|
||||||
result += fmt.Sprintf("%T:%v, ", v, v)
|
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)
|
||||||
}
|
}
|
||||||
return result + "]"
|
result += "]}"
|
||||||
|
case *matchRecursiveFn:
|
||||||
|
result += "{}"
|
||||||
|
result += pathString(fn.next)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertPathMatch(t *testing.T, path, ref QueryPath) bool {
|
func assertPathMatch(t *testing.T, path, ref *QueryPath) bool {
|
||||||
if len(path) != len(ref) {
|
pathStr := pathString(path.root)
|
||||||
t.Errorf("lengths do not match: %v vs %v",
|
refStr := pathString(ref.root)
|
||||||
pathString(path), pathString(ref))
|
if pathStr != refStr {
|
||||||
|
t.Errorf("paths do not match: %v vs %v")
|
||||||
|
t.Log("test:", pathStr)
|
||||||
|
t.Log("ref: ", refStr)
|
||||||
return false
|
return false
|
||||||
} else {
|
|
||||||
for i, v := range ref {
|
|
||||||
pass := false
|
|
||||||
node := path[i]
|
|
||||||
// compare by value
|
|
||||||
switch refNode := v.(type) {
|
|
||||||
case *matchKeyFn:
|
|
||||||
castNode, ok := node.(*matchKeyFn)
|
|
||||||
pass = ok && (*refNode == *castNode)
|
|
||||||
case *matchIndexFn:
|
|
||||||
castNode, ok := node.(*matchIndexFn)
|
|
||||||
pass = ok && (*refNode == *castNode)
|
|
||||||
case *matchSliceFn:
|
|
||||||
castNode, ok := node.(*matchSliceFn)
|
|
||||||
pass = ok && (*refNode == *castNode)
|
|
||||||
case *matchAnyFn:
|
|
||||||
castNode, ok := node.(*matchAnyFn)
|
|
||||||
pass = ok && (*refNode == *castNode)
|
|
||||||
case *matchUnionFn:
|
|
||||||
castNode, ok := node.(*matchUnionFn)
|
|
||||||
// special case - comapre all contents
|
|
||||||
pass = ok && assertPathMatch(t, castNode.Union, refNode.Union)
|
|
||||||
case *matchRecursiveFn:
|
|
||||||
castNode, ok := node.(*matchRecursiveFn)
|
|
||||||
pass = ok && (*refNode == *castNode)
|
|
||||||
}
|
|
||||||
if !pass {
|
|
||||||
t.Errorf("paths do not match at index %d: %v vs %v",
|
|
||||||
i, pathString(path), pathString(ref))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertPath(t *testing.T, query string, ref QueryPath) {
|
func assertPath(t *testing.T, query string, ref *QueryPath) {
|
||||||
_, flow := lex(query)
|
_, flow := lex(query)
|
||||||
path := parse(flow)
|
path := parse(flow)
|
||||||
assertPathMatch(t, path, ref)
|
assertPathMatch(t, path, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildPath(parts... PathFn) *QueryPath {
|
||||||
|
path := newQueryPath()
|
||||||
|
for _, v := range parts {
|
||||||
|
path.Append(v)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
func TestPathRoot(t *testing.T) {
|
func TestPathRoot(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$",
|
"$",
|
||||||
QueryPath{
|
buildPath(
|
||||||
// empty
|
// empty
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathKey(t *testing.T) {
|
func TestPathKey(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$.foo",
|
"$.foo",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchKeyFn{ "foo" },
|
newMatchKeyFn("foo"),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathBracketKey(t *testing.T) {
|
func TestPathBracketKey(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[foo]",
|
"$[foo]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchKeyFn{ "foo" },
|
newMatchKeyFn("foo"),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathBracketStringKey(t *testing.T) {
|
func TestPathBracketStringKey(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$['foo']",
|
"$['foo']",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchKeyFn{ "foo" },
|
newMatchKeyFn("foo"),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathIndex(t *testing.T) {
|
func TestPathIndex(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[123]",
|
"$[123]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchIndexFn{ 123 },
|
newMatchIndexFn(123),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathSliceStart(t *testing.T) {
|
func TestPathSliceStart(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[123:]",
|
"$[123:]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchSliceFn{ 123, math.MaxInt64, 1 },
|
newMatchSliceFn(123, math.MaxInt64, 1),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathSliceStartEnd(t *testing.T) {
|
func TestPathSliceStartEnd(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[123:456]",
|
"$[123:456]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchSliceFn{ 123, 456, 1 },
|
newMatchSliceFn(123, 456, 1),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathSliceStartEndColon(t *testing.T) {
|
func TestPathSliceStartEndColon(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[123:456:]",
|
"$[123:456:]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchSliceFn{ 123, 456, 1 },
|
newMatchSliceFn(123, 456, 1),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathSliceStartStep(t *testing.T) {
|
func TestPathSliceStartStep(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[123::7]",
|
"$[123::7]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchSliceFn{ 123, math.MaxInt64, 7 },
|
newMatchSliceFn(123, math.MaxInt64, 7),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathSliceEndStep(t *testing.T) {
|
func TestPathSliceEndStep(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[:456:7]",
|
"$[:456:7]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchSliceFn{ 0, 456, 7 },
|
newMatchSliceFn(0, 456, 7),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathSliceStep(t *testing.T) {
|
func TestPathSliceStep(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[::7]",
|
"$[::7]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchSliceFn{ 0, math.MaxInt64, 7 },
|
newMatchSliceFn(0, math.MaxInt64, 7),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathSliceAll(t *testing.T) {
|
func TestPathSliceAll(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[123:456:7]",
|
"$[123:456:7]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchSliceFn{ 123, 456, 7 },
|
newMatchSliceFn(123, 456, 7),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathAny(t *testing.T) {
|
func TestPathAny(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$.*",
|
"$.*",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchAnyFn{},
|
newMatchAnyFn(),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathUnion(t *testing.T) {
|
func TestPathUnion(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$[foo, bar, baz]",
|
"$[foo, bar, baz]",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchUnionFn{ []PathFn {
|
&matchUnionFn{ []PathFn {
|
||||||
&matchKeyFn{ "foo" },
|
newMatchKeyFn("foo"),
|
||||||
&matchKeyFn{ "bar" },
|
newMatchKeyFn("bar"),
|
||||||
&matchKeyFn{ "baz" },
|
newMatchKeyFn("baz"),
|
||||||
}},
|
}},
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathRecurse(t *testing.T) {
|
func TestPathRecurse(t *testing.T) {
|
||||||
assertPath(t,
|
assertPath(t,
|
||||||
"$..*",
|
"$..*",
|
||||||
QueryPath{
|
buildPath(
|
||||||
&matchRecursiveFn{},
|
newMatchRecursiveFn(),
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-18
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Based on the "jsonpath" spec/concept.
|
||||||
|
|
||||||
|
http://goessner.net/articles/JsonPath/
|
||||||
|
https://code.google.com/p/json-path/
|
||||||
|
*/
|
||||||
|
|
||||||
package jpath
|
package jpath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -8,7 +15,7 @@ import (
|
|||||||
type parser struct {
|
type parser struct {
|
||||||
flow chan token
|
flow chan token
|
||||||
tokensBuffer []token
|
tokensBuffer []token
|
||||||
path []PathFn
|
path *QueryPath
|
||||||
union []PathFn
|
union []PathFn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,10 +83,6 @@ func (p *parser) getToken() *token {
|
|||||||
return &tok
|
return &tok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) appendPath(fn PathFn) {
|
|
||||||
p.path = append(p.path, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseStart(p *parser) parserStateFn {
|
func parseStart(p *parser) parserStateFn {
|
||||||
tok := p.getToken()
|
tok := p.getToken()
|
||||||
|
|
||||||
@@ -99,12 +102,12 @@ func parseMatchExpr(p *parser) parserStateFn {
|
|||||||
tok := p.getToken()
|
tok := p.getToken()
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenDotDot:
|
case tokenDotDot:
|
||||||
p.appendPath(&matchRecursiveFn{})
|
p.path.Append(&matchRecursiveFn{})
|
||||||
// nested parse for '..'
|
// nested parse for '..'
|
||||||
tok := p.getToken()
|
tok := p.getToken()
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenKey:
|
case tokenKey:
|
||||||
p.appendPath(&matchKeyFn{tok.val})
|
p.path.Append(newMatchKeyFn(tok.val))
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
case tokenLBracket:
|
case tokenLBracket:
|
||||||
return parseBracketExpr
|
return parseBracketExpr
|
||||||
@@ -118,10 +121,10 @@ func parseMatchExpr(p *parser) parserStateFn {
|
|||||||
tok := p.getToken()
|
tok := p.getToken()
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenKey:
|
case tokenKey:
|
||||||
p.appendPath(&matchKeyFn{tok.val})
|
p.path.Append(newMatchKeyFn(tok.val))
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
case tokenStar:
|
case tokenStar:
|
||||||
p.appendPath(&matchAnyFn{})
|
p.path.Append(&matchAnyFn{})
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,11 +161,11 @@ loop: // labeled loop for easy breaking
|
|||||||
tok := p.getToken()
|
tok := p.getToken()
|
||||||
switch tok.typ {
|
switch tok.typ {
|
||||||
case tokenInteger:
|
case tokenInteger:
|
||||||
p.union = append(p.union, &matchIndexFn{tok.Int()})
|
p.union = append(p.union, newMatchIndexFn(tok.Int()))
|
||||||
case tokenKey:
|
case tokenKey:
|
||||||
p.union = append(p.union, &matchKeyFn{tok.val})
|
p.union = append(p.union, newMatchKeyFn(tok.val))
|
||||||
case tokenString:
|
case tokenString:
|
||||||
p.union = append(p.union, &matchKeyFn{tok.val})
|
p.union = append(p.union, newMatchKeyFn(tok.val))
|
||||||
case tokenQuestion:
|
case tokenQuestion:
|
||||||
return parseFilterExpr
|
return parseFilterExpr
|
||||||
case tokenLParen:
|
case tokenLParen:
|
||||||
@@ -184,9 +187,9 @@ loop: // labeled loop for easy breaking
|
|||||||
|
|
||||||
// if there is only one sub-expression, use that instead
|
// if there is only one sub-expression, use that instead
|
||||||
if len(p.union) == 1 {
|
if len(p.union) == 1 {
|
||||||
p.appendPath(p.union[0])
|
p.path.Append(p.union[0])
|
||||||
}else {
|
}else {
|
||||||
p.appendPath(&matchUnionFn{p.union})
|
p.path.Append(&matchUnionFn{p.union})
|
||||||
}
|
}
|
||||||
|
|
||||||
p.union = nil // clear out state
|
p.union = nil // clear out state
|
||||||
@@ -214,7 +217,7 @@ func parseSliceExpr(p *parser) parserStateFn {
|
|||||||
tok = p.getToken()
|
tok = p.getToken()
|
||||||
}
|
}
|
||||||
if tok.typ == tokenRBracket {
|
if tok.typ == tokenRBracket {
|
||||||
p.appendPath(&matchSliceFn{start, end, step})
|
p.path.Append(newMatchSliceFn(start, end, step))
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
}
|
}
|
||||||
if tok.typ != tokenColon {
|
if tok.typ != tokenColon {
|
||||||
@@ -234,7 +237,7 @@ func parseSliceExpr(p *parser) parserStateFn {
|
|||||||
p.raiseError(tok, "expected ']'")
|
p.raiseError(tok, "expected ']'")
|
||||||
}
|
}
|
||||||
|
|
||||||
p.appendPath(&matchSliceFn{start, end, step})
|
p.path.Append(newMatchSliceFn(start, end, step))
|
||||||
return parseMatchExpr
|
return parseMatchExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,11 +251,11 @@ func parseScriptExpr(p *parser) parserStateFn {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(flow chan token) []PathFn {
|
func parse(flow chan token) *QueryPath {
|
||||||
parser := &parser{
|
parser := &parser{
|
||||||
flow: flow,
|
flow: flow,
|
||||||
tokensBuffer: []token{},
|
tokensBuffer: []token{},
|
||||||
path: []PathFn{},
|
path: newQueryPath(),
|
||||||
}
|
}
|
||||||
parser.run()
|
parser.run()
|
||||||
return parser.path
|
return parser.path
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func assertQuery(t *testing.T, toml, query string, ref []interface{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
path := parse(flow)
|
path := parse(flow)
|
||||||
result := processPath(path, tree)
|
result := path.Call(tree)
|
||||||
assertValue(t, result, ref, "((" + query + ")) -> ")
|
assertValue(t, result, ref, "((" + query + ")) -> ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user