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