Skip to content

Commit 5c35b08

Browse files
committed
#6, #7, #8, #9
1 parent 993aeba commit 5c35b08

13 files changed

Lines changed: 308 additions & 31 deletions

File tree

README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,37 @@ import (
1616
)
1717
```
1818

19-
You can also test DFL filtering using a command line tool:
19+
You can also use the command line tool.
2020

2121
```
22-
Usage: dfl -filter INPUT [-verbose] [-version] [-help] [A=1] [B=2]
22+
Usage: dfl -f INPUT [-verbose] [-version] [-help] [-env] [A=1] [B=2]
2323
Options:
24-
-filter string
24+
-env
25+
Load environment variables
26+
-f string
2527
The DFL expression to evaulate
2628
-help
2729
Print help
2830
-verbose
2931
Provide verbose output
3032
-version
3133
Prints version to stdout
32-
3334
```
3435

3536
# Examples:
3637

38+
**Environment**
39+
40+
With the `-env` flag you can use DFL filters against the current environment.
41+
42+
```
43+
./dfl -env -f '@SHELL in [/bin/sh, /bin/bash]' && echo "Shell is set to sh or bash"
44+
```
45+
46+
**OpenStreetMap**
47+
48+
You can also use DFL to filter OpenStreetMap features.
49+
3750
```
3851
./dfl -verbose -filter '@pop > (10 - 2)' craft=brewery name=Stone pop=10
3952
true

cmd/dfl/main.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,22 @@ func main() {
6565

6666
var filter_text string
6767

68+
var load_env bool
6869
var verbose bool
6970
var version bool
7071
var help bool
7172

72-
flag.StringVar(&filter_text, "filter", "", "The DFL expression to evaulate")
73+
flag.StringVar(&filter_text, "f", "", "The DFL expression to evaulate")
7374

75+
flag.BoolVar(&load_env, "env", false, "Load environment variables")
7476
flag.BoolVar(&verbose, "verbose", false, "Provide verbose output")
7577
flag.BoolVar(&version, "version", false, "Prints version to stdout")
7678
flag.BoolVar(&help, "help", false, "Print help")
7779

7880
flag.Parse()
7981

8082
if help {
81-
fmt.Println("Usage: dfl -filter INPUT [-verbose] [-version] [-help] [A=1] [B=2]")
83+
fmt.Println("Usage: dfl -f INPUT [-verbose] [-version] [-help] [-env] [A=1] [B=2]")
8284
fmt.Println("Options:")
8385
flag.PrintDefaults()
8486
os.Exit(0)
@@ -87,7 +89,7 @@ func main() {
8789
fmt.Println("Run \"dfl -help\" for more information.")
8890
os.Exit(0)
8991
} else if len(os.Args) == 2 && os.Args[1] == "help" {
90-
fmt.Println("Usage: dfl -filter INPUT [-verbose] [-version] [-help] [A=1] [B=2]")
92+
fmt.Println("Usage: dfl -f INPUT [-verbose] [-version] [-help] [-env] [A=1] [B=2]")
9193
fmt.Println("Options:")
9294
flag.PrintDefaults()
9395
os.Exit(0)
@@ -99,13 +101,19 @@ func main() {
99101
}
100102

101103
ctx := map[string]interface{}{}
104+
if load_env {
105+
for _, e := range os.Environ() {
106+
pair := strings.Split(e, "=")
107+
ctx[pair[0]] = dfl.TryConvertString(pair[1])
108+
}
109+
}
102110
for _, a := range flag.Args() {
103111
if !strings.Contains(a, "=") {
104112
fmt.Println("Context attribute \"" + a + "\" does not contain \"=\".")
105113
os.Exit(1)
106114
}
107-
parts := strings.SplitN(a, "=", 2)
108-
ctx[parts[0]] = dfl.TryConvertString(parts[1])
115+
pair := strings.SplitN(a, "=", 2)
116+
ctx[pair[0]] = dfl.TryConvertString(pair[1])
109117
}
110118

111119
root, err := dfl.Parse(filter_text)

dfl/Array.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
)
13+
14+
// Array is a Node representing an array of values, which can be either a Literal or Attribute.
15+
type Array struct {
16+
Nodes []Node
17+
}
18+
19+
func (a Array) Dfl() string {
20+
str := "["
21+
for i, x := range a.Nodes {
22+
if i > 0 {
23+
str += ", "
24+
}
25+
str += x.Dfl()
26+
}
27+
str = str + "]"
28+
return str
29+
}
30+
31+
func (a Array) Map() map[string]interface{} {
32+
return map[string]interface{}{
33+
"nodes": a.Nodes,
34+
}
35+
}
36+
37+
// Compile returns a compiled version of this node.
38+
// If all the values of an Set are literals, returns a single Literal with the corresponding Set/slice as its value.
39+
// Otherwise returns the original node..
40+
func (a Array) Compile() Node {
41+
values := make([]interface{}, len(a.Nodes))
42+
nodes := reflect.ValueOf(a.Nodes)
43+
for i := 0; i < nodes.Len(); i++ {
44+
n := nodes.Index(i).Interface()
45+
switch n.(type) {
46+
case *Literal:
47+
values[i] = n.(*Literal).Value
48+
default:
49+
return a
50+
}
51+
}
52+
return Literal{Value: values}
53+
}
54+
55+
func (a Array) Evaluate(ctx Context, funcs FunctionMap) (interface{}, error) {
56+
values := make([]interface{}, len(a.Nodes))
57+
for i, n := range a.Nodes {
58+
v, err := n.Evaluate(ctx, funcs)
59+
if err != nil {
60+
return values, err
61+
}
62+
values[i] = v
63+
}
64+
return values, nil
65+
}
66+
67+
func (a Array) Attributes() []string {
68+
set := make(map[string]struct{})
69+
for _, n := range a.Nodes {
70+
for _, x := range n.Attributes() {
71+
set[x] = struct{}{}
72+
}
73+
}
74+
attrs := make([]string, 0, len(set))
75+
for x := range set {
76+
attrs = append(attrs, x)
77+
}
78+
return attrs
79+
}

dfl/EvaluateBool.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package dfl
22

33
import (
4-
"fmt"
4+
"fmt"
55
"reflect"
66
)
77

@@ -11,15 +11,15 @@ import (
1111

1212
// EvaluateBool returns the boolean value of a node given a context. If the result is not a bool, then returns an error.
1313
func EvaluateBool(n Node, ctx Context, funcs FunctionMap) (bool, error) {
14-
result, err := n.Evaluate(ctx, funcs)
15-
if err != nil {
16-
return false, errors.Wrap(err, "Error evaluating expression")
17-
}
14+
result, err := n.Evaluate(ctx, funcs)
15+
if err != nil {
16+
return false, errors.Wrap(err, "Error evaluating expression")
17+
}
1818

19-
switch result.(type) {
20-
case bool:
21-
return result.(bool), nil
22-
}
19+
switch result.(type) {
20+
case bool:
21+
return result.(bool), nil
22+
}
2323

24-
return false, errors.New("Evaluation returned a "+fmt.Sprint(reflect.TypeOf(result))+" instead of bool")
24+
return false, errors.New("Evaluation returned a " + fmt.Sprint(reflect.TypeOf(result)) + " instead of bool")
2525
}

dfl/EvaluateInt.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dfl
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
)
7+
8+
import (
9+
"github.com/pkg/errors"
10+
)
11+
12+
// EvaluateInt returns the int value of a node given a context. If the result is not an int, then returns an error.
13+
func EvaluateInt(n Node, ctx Context, funcs FunctionMap) (int, error) {
14+
result, err := n.Evaluate(ctx, funcs)
15+
if err != nil {
16+
return 0, errors.Wrap(err, "Error evaluating expression")
17+
}
18+
19+
switch result.(type) {
20+
case int:
21+
return result.(int), nil
22+
}
23+
24+
return 0, errors.New("Evaluation returned a " + fmt.Sprint(reflect.TypeOf(result)) + " instead of int")
25+
}

dfl/EvaluateString.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dfl
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
)
7+
8+
import (
9+
"github.com/pkg/errors"
10+
)
11+
12+
// EvaluateString returns the string value of a node given a context. If the result is not a string, then returns an error.
13+
func EvaluateString(n Node, ctx Context, funcs FunctionMap) (string, error) {
14+
result, err := n.Evaluate(ctx, funcs)
15+
if err != nil {
16+
return "", errors.Wrap(err, "Error evaluating expression")
17+
}
18+
19+
switch result.(type) {
20+
case string:
21+
return result.(string), nil
22+
}
23+
24+
return "", errors.New("Evaluation returned a " + fmt.Sprint(reflect.TypeOf(result)) + " instead of string")
25+
}

dfl/In_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ func TestIn(t *testing.T) {
2424
NewTestCase("bar in @b", ctx, true),
2525
NewTestCase("@a in @b", ctx, true),
2626
NewTestCase("bar in [bar, cafe]", ctx, true),
27+
NewTestCase("bar in {bar, cafe}", ctx, true),
2728
NewTestCase("fast_food in [bar, cafe]", ctx, false),
29+
NewTestCase("fast_food in {bar, cafe}", ctx, false),
2830
NewTestCase("fast_food in @b", ctx, false),
2931
}
3032

dfl/Parse.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func Parse(in string) (Node, error) {
3636
} else {
3737

3838
parentheses := 0
39+
curlybrackets := 0
3940
squarebrackets := 0
4041
singlequotes := 0
4142
doublequotes := 0
@@ -53,6 +54,10 @@ func Parse(in string) (Node, error) {
5354
squarebrackets += 1
5455
} else if squarebrackets == 1 && c == ']' {
5556
squarebrackets -= 1
57+
} else if curlybrackets == 0 && c == '{' {
58+
curlybrackets += 1
59+
} else if curlybrackets == 1 && c == '}' {
60+
curlybrackets -= 1
5661
} else if singlequotes == 1 && c == '\'' {
5762
singlequotes -= 1
5863
} else if singlequotes == 0 && c == '\'' {
@@ -63,10 +68,12 @@ func Parse(in string) (Node, error) {
6368
doublequotes += 1
6469
}
6570

66-
if parentheses == 0 && squarebrackets == 0 && singlequotes == 0 && doublequotes == 0 && (len(remainder) == 0 || in[i+1] == ' ') {
71+
if parentheses == 0 && squarebrackets == 0 && curlybrackets == 0 && singlequotes == 0 && doublequotes == 0 && (len(remainder) == 0 || in[i+1] == ' ') {
6772
if len(s) >= 2 && ((strings.HasPrefix(s, "'") && strings.HasSuffix(s, "'")) || (strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\""))) {
6873
return ParseLiteral(s[1:len(s)-1], remainder)
6974
} else if len(s) >= 2 && strings.HasPrefix(s, "[") && strings.HasSuffix(s, "]") {
75+
return ParseArray(s[1:len(s)-1], remainder)
76+
} else if len(s) >= 2 && strings.HasPrefix(s, "{") && strings.HasSuffix(s, "}") {
7077
return ParseSet(s[1:len(s)-1], remainder)
7178
} else if len(s) >= 2 && strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") {
7279
return ParseSub(s[1:len(s)-1], remainder)

0 commit comments

Comments
 (0)