Refactored match to use function chaining

This commit is contained in:
eanderton
2014-09-07 16:32:29 -04:00
parent a98788e0d7
commit c81f1892c2
4 changed files with 232 additions and 145 deletions
+91 -88
View File
@@ -6,181 +6,184 @@ import (
"testing"
)
func pathString(path QueryPath) string {
result := "["
for _, v := range path {
result += fmt.Sprintf("%T:%v, ", v, v)
// dump path tree to a string
func pathString(root PathFn) string {
result := fmt.Sprintf("%T:")
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)
}
return result + "]"
return result
}
func assertPathMatch(t *testing.T, path, ref QueryPath) bool {
if len(path) != len(ref) {
t.Errorf("lengths do not match: %v vs %v",
pathString(path), pathString(ref))
func assertPathMatch(t *testing.T, path, ref *QueryPath) bool {
pathStr := pathString(path.root)
refStr := pathString(ref.root)
if pathStr != refStr {
t.Errorf("paths do not match: %v vs %v")
t.Log("test:", pathStr)
t.Log("ref: ", refStr)
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
}
func assertPath(t *testing.T, query string, ref QueryPath) {
func assertPath(t *testing.T, query string, ref *QueryPath) {
_, flow := lex(query)
path := parse(flow)
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) {
assertPath(t,
"$",
QueryPath{
buildPath(
// empty
})
))
}
func TestPathKey(t *testing.T) {
assertPath(t,
"$.foo",
QueryPath{
&matchKeyFn{ "foo" },
})
buildPath(
newMatchKeyFn("foo"),
))
}
func TestPathBracketKey(t *testing.T) {
assertPath(t,
"$[foo]",
QueryPath{
&matchKeyFn{ "foo" },
})
buildPath(
newMatchKeyFn("foo"),
))
}
func TestPathBracketStringKey(t *testing.T) {
assertPath(t,
"$['foo']",
QueryPath{
&matchKeyFn{ "foo" },
})
buildPath(
newMatchKeyFn("foo"),
))
}
func TestPathIndex(t *testing.T) {
assertPath(t,
"$[123]",
QueryPath{
&matchIndexFn{ 123 },
})
buildPath(
newMatchIndexFn(123),
))
}
func TestPathSliceStart(t *testing.T) {
assertPath(t,
"$[123:]",
QueryPath{
&matchSliceFn{ 123, math.MaxInt64, 1 },
})
buildPath(
newMatchSliceFn(123, math.MaxInt64, 1),
))
}
func TestPathSliceStartEnd(t *testing.T) {
assertPath(t,
"$[123:456]",
QueryPath{
&matchSliceFn{ 123, 456, 1 },
})
buildPath(
newMatchSliceFn(123, 456, 1),
))
}
func TestPathSliceStartEndColon(t *testing.T) {
assertPath(t,
"$[123:456:]",
QueryPath{
&matchSliceFn{ 123, 456, 1 },
})
buildPath(
newMatchSliceFn(123, 456, 1),
))
}
func TestPathSliceStartStep(t *testing.T) {
assertPath(t,
"$[123::7]",
QueryPath{
&matchSliceFn{ 123, math.MaxInt64, 7 },
})
buildPath(
newMatchSliceFn(123, math.MaxInt64, 7),
))
}
func TestPathSliceEndStep(t *testing.T) {
assertPath(t,
"$[:456:7]",
QueryPath{
&matchSliceFn{ 0, 456, 7 },
})
buildPath(
newMatchSliceFn(0, 456, 7),
))
}
func TestPathSliceStep(t *testing.T) {
assertPath(t,
"$[::7]",
QueryPath{
&matchSliceFn{ 0, math.MaxInt64, 7 },
})
buildPath(
newMatchSliceFn(0, math.MaxInt64, 7),
))
}
func TestPathSliceAll(t *testing.T) {
assertPath(t,
"$[123:456:7]",
QueryPath{
&matchSliceFn{ 123, 456, 7 },
})
buildPath(
newMatchSliceFn(123, 456, 7),
))
}
func TestPathAny(t *testing.T) {
assertPath(t,
"$.*",
QueryPath{
&matchAnyFn{},
})
buildPath(
newMatchAnyFn(),
))
}
func TestPathUnion(t *testing.T) {
assertPath(t,
"$[foo, bar, baz]",
QueryPath{
buildPath(
&matchUnionFn{ []PathFn {
&matchKeyFn{ "foo" },
&matchKeyFn{ "bar" },
&matchKeyFn{ "baz" },
newMatchKeyFn("foo"),
newMatchKeyFn("bar"),
newMatchKeyFn("baz"),
}},
})
))
}
func TestPathRecurse(t *testing.T) {
assertPath(t,
"$..*",
QueryPath{
&matchRecursiveFn{},
})
buildPath(
newMatchRecursiveFn(),
))
}