gofmt pass

This commit is contained in:
eanderton
2014-09-09 04:09:36 -04:00
parent 12e974f892
commit 7f30fba1e6
7 changed files with 548 additions and 551 deletions
+8 -8
View File
@@ -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) {
-1
View File
@@ -97,4 +97,3 @@ func TestLexSpace(t *testing.T) {
token{Position{1, 12}, tokenEOF, ""},
})
}
+124 -125
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
},
}