Skip to content

Commit ac7d861

Browse files
authored
Merge pull request #25 from spatialcurrent/aug5_update
Multi-line Expressions and Other Enhancements
2 parents 27ab2aa + c60f6ec commit ac7d861

30 files changed

Lines changed: 1275 additions & 451 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ With the `-env` flag you can use DFL filters against the current environment.
7070
You can use DFL to filter integer arrays.
7171

7272
```
73-
./dfl -verbose -f '@a == [1, 2, 3, 4]' 'a=[1, 2, 3, 4]'
73+
./dfl -f '@a == [1, 2, 3, 4]' 'a=[1, 2, 3, 4]'
7474
# returns true as exit code 0
7575
```
7676

@@ -86,7 +86,7 @@ You can also use DFl to compare byte arrays.
8686
You can use DFL to filter IP addresses.
8787

8888
```
89-
./dfl -verbose -f '@ip in 10.10.0.0/16' ip=10.10.20.22
89+
./dfl -f '@ip in 10.10.0.0/16' ip=10.10.20.22
9090
# returns true as exit code 0
9191
```
9292

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ var GO_DFL_VERSION = "0.0.3"
3030

3131
type Node struct {
3232
Node dfl.Node
33+
FunctionMap dfl.FunctionMap
3334
}
3435

3536
func (n Node) Compile() *js.Object {
36-
return js.MakeWrapper(Node{Node:n.Node.Compile()})
37+
return js.MakeWrapper(Node{
38+
Node: n.Node.Compile(),
39+
FunctionMap: dfl.NewFuntionMapWithDefaults(),
40+
})
3741
}
3842

3943
func (n Node) Evaluate(options *js.Object) interface{} {
@@ -43,7 +47,7 @@ func (n Node) Evaluate(options *js.Object) interface{} {
4347
ctx[key] = options.Get(key).Interface()
4448
}
4549

46-
result, err := n.Node.Evaluate(ctx, dfl.FunctionMap{})
50+
result, err := n.Node.Evaluate(ctx, n.FunctionMap)
4751
if err != nil {
4852
console.Log(err.Error())
4953
return false
@@ -87,7 +91,7 @@ func EvaluateBool(s string, options *js.Object) bool {
8791
ctx[key] = options.Get(key).Interface()
8892
}
8993

90-
result, err := dfl.EvaluateBool(root, ctx, dfl.FunctionMap{})
94+
result, err := dfl.EvaluateBool(root, ctx, dfl.NewFuntionMapWithDefaults())
9195
if err != nil {
9296
console.Log(err.Error())
9397
return false
@@ -110,7 +114,7 @@ func EvaluateInt(s string, options *js.Object) int {
110114
ctx[key] = options.Get(key).Interface()
111115
}
112116

113-
result, err := dfl.EvaluateInt(root, ctx, dfl.FunctionMap{})
117+
result, err := dfl.EvaluateInt(root, ctx, dfl.NewFuntionMapWithDefaults())
114118
if err != nil {
115119
console.Log(err.Error())
116120
return 0
@@ -133,7 +137,7 @@ func EvaluateFloat64(s string, options *js.Object) float64 {
133137
ctx[key] = options.Get(key).Interface()
134138
}
135139

136-
result, err := dfl.EvaluateFloat64(root, ctx, dfl.FunctionMap{})
140+
result, err := dfl.EvaluateFloat64(root, ctx, dfl.NewFuntionMapWithDefaults())
137141
if err != nil {
138142
console.Log(err.Error())
139143
return 0.0
@@ -156,7 +160,7 @@ func EvaluateString(s string, options *js.Object) string {
156160
ctx[key] = options.Get(key).Interface()
157161
}
158162

159-
result, err := dfl.EvaluateString(root, ctx, dfl.FunctionMap{})
163+
result, err := dfl.EvaluateString(root, ctx, dfl.NewFuntionMapWithDefaults())
160164
if err != nil {
161165
console.Log(err.Error())
162166
return ""

cmd/dfl/main.go

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,6 @@ import (
4646

4747
var GO_DFL_VERSION = "0.0.3"
4848

49-
func dfl_build_funcs() dfl.FunctionMap {
50-
funcs := dfl.FunctionMap{}
51-
52-
funcs["len"] = func(ctx dfl.Context, args []string) (interface{}, error) {
53-
if len(args) != 1 {
54-
return 0, errors.New("Invalid number of arguments to len.")
55-
}
56-
return len(args[0]), nil
57-
}
58-
59-
return funcs
60-
}
61-
6249
func main() {
6350

6451
start := time.Now()
@@ -171,8 +158,7 @@ func main() {
171158
fmt.Println(string(out))
172159
}
173160

174-
funcs := dfl_build_funcs()
175-
result, err := root.Evaluate(ctx, funcs)
161+
result, err := root.Evaluate(ctx, dfl.NewFuntionMapWithDefaults())
176162
if err != nil {
177163
fmt.Println("Error evaluating expression.")
178164
fmt.Println(err)

dfl/And_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestAnd(t *testing.T) {
3838
if err != nil {
3939
t.Errorf(errors.Wrap(err, "Error evaluating expression \""+testCase.Expression+"\"").Error())
4040
} else if got != testCase.Result {
41-
t.Errorf("TestSubtract(%q) == %v (%q), want %v (%q)", testCase.Expression, got, reflect.TypeOf(got), testCase.Result, reflect.TypeOf(testCase.Result))
41+
t.Errorf("TestAnd(%q) == %v (%q), want %v (%q)", testCase.Expression, got, reflect.TypeOf(got), testCase.Result, reflect.TypeOf(testCase.Result))
4242
}
4343
}
4444

dfl/AttachLeft.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// =================================================================
2+
//
3+
// Copyright (C) 2018 Spatial Current, Inc. - All Rights Reserved
4+
// Released as open source under the MIT License. See LICENSE file.
5+
//
6+
// =================================================================
7+
8+
package dfl
9+
10+
import (
11+
"github.com/pkg/errors"
12+
)
13+
14+
// AttachLeft attaches the left Node as the left child node to the parent root Node.
15+
func AttachLeft(root Node, left Node) error {
16+
switch root.(type) {
17+
case *And:
18+
root.(*And).Left = left
19+
case *Or:
20+
root.(*Or).Left = left
21+
case *Xor:
22+
root.(*Xor).Left = left
23+
case *Coalesce:
24+
root.(*Coalesce).Left = left
25+
case *In:
26+
root.(*In).Left = left
27+
case *Like:
28+
root.(*Like).Left = left
29+
case *ILike:
30+
root.(*ILike).Left = left
31+
case *LessThan:
32+
root.(*LessThan).Left = left
33+
case *LessThanOrEqual:
34+
root.(*LessThanOrEqual).Left = left
35+
case *GreaterThan:
36+
root.(*GreaterThan).Left = left
37+
case *GreaterThanOrEqual:
38+
root.(*GreaterThanOrEqual).Left = left
39+
case *Equal:
40+
root.(*Equal).Left = left
41+
case *NotEqual:
42+
root.(*NotEqual).Left = left
43+
case *Add:
44+
root.(*Add).Left = left
45+
case *Subtract:
46+
root.(*Subtract).Left = left
47+
case *Before:
48+
root.(*Before).Left = left
49+
case *After:
50+
root.(*After).Left = left
51+
default:
52+
return errors.New("Could not attach left as root is not a binary operator")
53+
}
54+
return nil
55+
}

dfl/Attribute.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
package dfl
99

10-
// Array is a Node representing the value of an attribute in the context map.
10+
// Attribute is a Node representing the value of an attribute in the context map.
11+
// Attributes start with a "@" and follow with the name or full path into the object if multiple levels deep.
12+
// For example, @a and @a.b.c.d. You can also use a null-safe operator, e.g., @a?.b?.c?.d
1113
type Attribute struct {
1214
Name string
1315
}
@@ -23,15 +25,11 @@ func (a Attribute) Map() map[string]interface{} {
2325
}
2426

2527
func (a Attribute) Compile() Node {
26-
return a
28+
return Attribute{Name: a.Name}
2729
}
2830

2931
func (a Attribute) Evaluate(ctx Context, funcs FunctionMap) (interface{}, error) {
30-
if v, ok := ctx[a.Name]; ok {
31-
return v, nil
32-
} else {
33-
return "", nil
34-
}
32+
return Extract(a.Name, ctx)
3533
}
3634

3735
func (a Attribute) Attributes() []string {

dfl/Attribute_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// =================================================================
2+
//
3+
// Copyright (C) 2018 Spatial Current, Inc. - All Rights Reserved
4+
// Released as open source under the MIT License. See LICENSE file.
5+
//
6+
// =================================================================
7+
8+
package dfl
9+
10+
import (
11+
"reflect"
12+
"testing"
13+
)
14+
15+
import (
16+
"github.com/pkg/errors"
17+
)
18+
19+
func TestAttribute(t *testing.T) {
20+
21+
ctx := Context{
22+
"a": nil,
23+
"b": nil,
24+
"c": map[string]interface{}{"d": 10, "e": map[string]interface{}{"f": 100}},
25+
"g": []int{10, 20, 30},
26+
"h": map[string]interface{}{"i": []int{10, 20, 30, 40}},
27+
"j": "bars",
28+
"k": []map[string]interface{}{
29+
map[string]interface{}{"l": "m"},
30+
},
31+
}
32+
33+
testCases := []TestCase{
34+
NewTestCase("(@a?.d ?: 10) == 10", ctx, true),
35+
NewTestCase("@c?.d == 10", ctx, true),
36+
NewTestCase("@c.d == 10", ctx, true),
37+
NewTestCase("@c.e.f == 100", ctx, true),
38+
NewTestCase("@c.e?.f == 100", ctx, true),
39+
NewTestCase("@g[0] == 10", ctx, true),
40+
NewTestCase("@g[0:2] == [10,20]", ctx, true),
41+
NewTestCase("@h?.i[1:3] == [20,30]", ctx, true),
42+
NewTestCase("@h?.i[1:3] == [20,30]", ctx, true),
43+
NewTestCase("@j[0:3] == bar", ctx, true),
44+
NewTestCase("@j[:3] == bar", ctx, true),
45+
NewTestCase("@k[0]l == m", ctx, true),
46+
}
47+
48+
for _, testCase := range testCases {
49+
node, err := Parse(testCase.Expression)
50+
if err != nil {
51+
t.Errorf(errors.Wrap(err, "Error parsing expression \""+testCase.Expression+"\"").Error())
52+
continue
53+
}
54+
node = node.Compile()
55+
got, err := node.Evaluate(testCase.Context, FunctionMap{})
56+
if err != nil {
57+
t.Errorf(errors.Wrap(err, "Error evaluating expression \""+testCase.Expression+"\"").Error())
58+
} else if got != testCase.Result {
59+
t.Errorf("TestAttribute(%q) == %v (%q), want %v (%q)", testCase.Expression, got, reflect.TypeOf(got), testCase.Result, reflect.TypeOf(testCase.Result))
60+
}
61+
}
62+
63+
}

dfl/Coalesce.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// =================================================================
2+
//
3+
// Copyright (C) 2018 Spatial Current, Inc. - All Rights Reserved
4+
// Released as open source under the MIT License. See LICENSE file.
5+
//
6+
// =================================================================
7+
8+
package dfl
9+
10+
import (
11+
"github.com/pkg/errors"
12+
)
13+
14+
// Coalesce is a BinaryOperator which returns the left value if not null otherwise the right value.
15+
type Coalesce struct {
16+
*BinaryOperator
17+
}
18+
19+
func (c Coalesce) Dfl() string {
20+
return "(" + c.Left.Dfl() + " ?: " + c.Right.Dfl() + ")"
21+
}
22+
23+
func (c Coalesce) Map() map[string]interface{} {
24+
return map[string]interface{}{
25+
"op": "?:",
26+
"left": c.Left.Map(),
27+
"right": c.Right.Map(),
28+
}
29+
}
30+
31+
// Compile returns a compiled version of this node.
32+
// If the left value is compiled as a Literal, then returns the left value.
33+
// Otherwise, returns a clone.
34+
func (c Coalesce) Compile() Node {
35+
left := c.Left.Compile()
36+
switch left.(type) {
37+
case Literal:
38+
return Literal{Value: left.(Literal).Value}
39+
}
40+
right := c.Right.Compile()
41+
return Coalesce{&BinaryOperator{Left: left, Right: right}}
42+
}
43+
44+
func (c Coalesce) Evaluate(ctx Context, funcs FunctionMap) (interface{}, error) {
45+
lv, err := c.Left.Evaluate(ctx, funcs)
46+
if err != nil {
47+
return lv, errors.Wrap(err, "Error evaluating Coalesce left value")
48+
}
49+
50+
switch lv.(type) {
51+
case Null:
52+
rv, err := c.Right.Evaluate(ctx, funcs)
53+
if err != nil {
54+
return rv, errors.Wrap(err, "Error evaluating Coalesce right value")
55+
}
56+
return rv, nil
57+
}
58+
59+
return lv, nil
60+
}

dfl/Coalesce_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// =================================================================
2+
//
3+
// Copyright (C) 2018 Spatial Current, Inc. - All Rights Reserved
4+
// Released as open source under the MIT License. See LICENSE file.
5+
//
6+
// =================================================================
7+
8+
package dfl
9+
10+
import (
11+
"reflect"
12+
"testing"
13+
)
14+
15+
import (
16+
"github.com/pkg/errors"
17+
)
18+
19+
func TestCoalesce(t *testing.T) {
20+
21+
ctx := Context{"a": nil, "b": 10, "c": 10}
22+
23+
testCases := []TestCase{
24+
NewTestCase("(@a ?: 10) == 10", ctx, true),
25+
NewTestCase("(@a ?: @b) == 10", ctx, true),
26+
NewTestCase("(@a ?: @Z ?: @c) == 10", ctx, true),
27+
NewTestCase("(@a ?: @Z ?: @c) == 15", ctx, false),
28+
}
29+
30+
for _, testCase := range testCases {
31+
node, err := Parse(testCase.Expression)
32+
if err != nil {
33+
t.Errorf(errors.Wrap(err, "Error parsing expression \""+testCase.Expression+"\"").Error())
34+
continue
35+
}
36+
node = node.Compile()
37+
got, err := node.Evaluate(testCase.Context, FunctionMap{})
38+
if err != nil {
39+
t.Errorf(errors.Wrap(err, "Error evaluating expression \""+testCase.Expression+"\"").Error())
40+
} else if got != testCase.Result {
41+
t.Errorf("TestCoalesce(%q) == %v (%q), want %v (%q)", testCase.Expression, got, reflect.TypeOf(got), testCase.Result, reflect.TypeOf(testCase.Result))
42+
}
43+
}
44+
45+
}

dfl/Counter.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
// =================================================================
2+
//
3+
// Copyright (C) 2018 Spatial Current, Inc. - All Rights Reserved
4+
// Released as open source under the MIT License. See LICENSE file.
5+
//
6+
// =================================================================
7+
18
package dfl
29

10+
// Counter is used for creating a frequency histogram of values
311
type Counter map[string]int
412

513
func (c Counter) Len() int {

0 commit comments

Comments
 (0)