Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 90 additions & 3 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import (
"sort"
"strconv"
"strings"
"fmt"
"regexp"
"go/types"
"go/token"
)

type Applicator interface {
Expand Down Expand Up @@ -114,7 +118,7 @@ func (w *WildCardSelection) Apply(v interface{}) (interface{}, error) {
rval, err := applyNext(w.NextNode, tv[key])
// Don't add anything that causes an error or returns nil.
if err == nil || rval != nil {
ret = append(ret, rval)
ret = flattenAppend(ret, rval)
}
}
return ret, nil
Expand All @@ -124,7 +128,7 @@ func (w *WildCardSelection) Apply(v interface{}) (interface{}, error) {
rval, err := applyNext(w.NextNode, val)
// Don't add anything that causes an error or returns nil.
if err == nil || rval != nil {
ret = append(ret, rval)
ret = flattenAppend(ret, rval)
}
}
return ret, nil
Expand All @@ -134,6 +138,52 @@ func (w *WildCardSelection) Apply(v interface{}) (interface{}, error) {
}
}

type WildCardFilterSelection struct {
RootNode
Key string
}

func (w *WildCardFilterSelection) Apply(v interface{}) (interface{}, error) {
arv, ok := v.([]interface{})
if !ok {
return v, ArrayTypeError
}
var ret []interface{}
for _, val := range arv {
_, ok := val.(map[string]interface{})
if !ok {
return v, MapTypeError
}

re, err := regexp.Compile(`[\S]+`)
if err != nil {
return v, err
}
ops := re.FindAllString(w.Key, -1)
wa, _ := Parse(strings.Replace(ops[0], "@", "$", 1))
subv, _ := wa.Apply(val)
if subv == nil {
continue
}

if len(ops) == 3 {
isOk, _ := cmp_any(subv, ops[2], ops[1])
if !isOk {
continue
}
}

rval, err := applyNext(w.NextNode, val)

// Don't add anything that causes an error or returns nil.
if err == nil || rval != nil {
ret = append(ret, rval)
}
}
return ret, nil
}


// DescentSelection is a filter that recursively descends applying it's NextNode and
// corrlating the results.
type DescentSelection struct {
Expand Down Expand Up @@ -291,7 +341,7 @@ func getNode(s string) (node, string, error) {
case "[.":
return &DescentSelection{}, rs, nil
case "[?", "[(":
return nil, rs, NotSupportedError
return &WildCardFilterSelection{Key: s[3 : n-1]}, rs, nil
default: // Assume it's a array index otherwise.
i, err := strconv.Atoi(s[1:n])
if err != nil {
Expand Down Expand Up @@ -322,3 +372,40 @@ func Parse(s string) (Applicator, error) {
}
return &rt, nil
}


func cmp_any(obj1, obj2 interface{}, op string) (bool, error) {
switch op {
case "<", "<=", "==", ">=", ">", "!=":
default:
return false, fmt.Errorf("op should only be <, <=, ==, !=, >= and >")
}
var sobj1 string
switch obj1.(type) {
case string:
sobj1 = fmt.Sprintf("\"%v\"", obj1)
default:
sobj1 = fmt.Sprintf("%v", obj1)
}
var sobj2 string
switch obj1.(type) {
case string:
sobj2 = fmt.Sprintf("\"%v\"", obj2)
default:
sobj2 = fmt.Sprintf("%v", obj2)
}

exp := fmt.Sprintf("%v %s %v", sobj1, op, sobj2)
fset := token.NewFileSet()
res, err := types.Eval(fset, nil, 0, exp)
if err != nil {
return false, err
}
if res.IsValue() == false || (res.Value.String() != "false" && res.Value.String() != "true") {
return false, fmt.Errorf("result should only be true or false")
}
if res.Value.String() == "true" {
return true, nil
}
return false, nil
}
45 changes: 43 additions & 2 deletions parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ func isSameWildcardSelectionNode(n *WildCardSelection, m node) bool {
return false
}
}
func isSameWildcardFilterSelectionNode(n *WildCardFilterSelection, m node) bool {
switch mv := m.(type) {
case *WildCardFilterSelection:
return isSameNode(n.NextNode, mv.NextNode)
default:
return false
}
}
func isSameDescentSelectionNode(n *DescentSelection, m node) bool {
switch mv := m.(type) {
case *DescentSelection:
Expand All @@ -110,6 +118,8 @@ func isSameNode(n, m node) bool {
return isSameDescentSelectionNode(nv, m)
case *WildCardSelection:
return isSameWildcardSelectionNode(nv, m)
case *WildCardFilterSelection:
return isSameWildcardFilterSelectionNode(nv, m)
default:
return false
}
Expand All @@ -126,8 +136,8 @@ func TestGetNode(t *testing.T) {
{t: `[10]`, n: &ArraySelection{Key: 10}},
{t: `[*]`, n: &WildCardSelection{}},
{t: `[..]`, n: &DescentSelection{}},
{t: `[?(@.lenght())]`, n: nil, err: NotSupportedError},
{t: `[(@.foo)]`, n: nil, err: NotSupportedError},
{t: `[?(@.lenght())]`, n: &WildCardFilterSelection{Key: "@.lenght()"}},
{t: `[(@.foo)]`, n: &WildCardFilterSelection{Key: "@.foo"}},
{t: `[0:10:2]`, n: nil, err: SyntaxError},
}
for i, test := range testcases {
Expand Down Expand Up @@ -257,6 +267,37 @@ func TestParse(t *testing.T) {
t: "..author..",
expected: []interface{}{"Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"},
},
{
t: "store.book[?(@.price < 10)]",
expected: []interface{}{ map[string]interface{}{
"category": "reference",
"category.sub": "quotes",
"author": "Nigel Rees",
"title": "Saying of the Century",
"price": 8.95,
}, map[string]interface{}{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99,
},

},
},
{
t: `store.book[?(@.category == reference)]`,
expected: []interface{}{ map[string]interface{}{
"category": "reference",
"category.sub": "quotes",
"author": "Nigel Rees",
"title": "Saying of the Century",
"price": 8.95,
},

},
},

}
for i, test := range testcases {
a, err := Parse(test.t)
Expand Down