...
1# Gval
2
3[](https://pkg.go.dev/github.com/PaesslerAG/gval)
4[](https://travis-ci.org/PaesslerAG/gval)
5[](https://coveralls.io/github/PaesslerAG/gval?branch=master)
6[](https://goreportcard.com/report/github.com/PaesslerAG/gval)
7
8Gval (Go eVALuate) provides support for evaluating arbitrary expressions, in particular Go-like expressions.
9
10
11
12## Evaluate
13
14Gval can evaluate expressions with parameters, arimethetic, logical, and string operations:
15
16- basic expression: [10 > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Basic)
17- parameterized expression: [foo > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Parameter)
18- nested parameterized expression: [foo.bar > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedParameter)
19- arithmetic expression: [(requests_made * requests_succeeded / 100) >= 90](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Arithmetic)
20- string expression: [http_response_body == "service is ok"](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-String)
21- float64 expression: [(mem_used / total_mem) * 100](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Float64)
22
23It can easily be extended with custom functions or operators:
24
25- custom date comparator: [date(\`2014-01-02\`) > date(\`2014-01-01 23:59:59\`)](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-DateComparison)
26- string length: [strlen("someReallyLongInputString") <= 16](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Strlen)
27
28You can parse gval.Expressions once and re-use them multiple times. Parsing is the compute-intensive phase of the process, so if you intend to use the same expression with different parameters, just parse it once:
29
30- [Parsing and Evaluation](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluable)
31
32The normal Go-standard order of operators is respected. When writing an expression, be sure that you either order the operators correctly, or use parentheses to clarify which portions of an expression should be run first.
33
34Strings, numbers, and booleans can be used like in Go:
35
36- [(7 < "47" == true ? "hello world!\n\u263a") + \` more text\`](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Encoding)
37
38## Parameter
39
40Variables can be accessed via string literals. They can be used for values with string keys if the parameter is a `map[string]interface{}` or `map[interface{}]interface{}` and for fields or methods if the parameter is a struct.
41
42- [foo > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Parameter)
43
44### Bracket Selector
45
46Map and array elements and Struct Field can be accessed via `[]`.
47
48- [foo[0]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Array)
49- [foo["b" + "a" + "r"]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-ExampleEvaluate_ComplexAccessor)
50
51### Dot Selector
52
53A nested variable with a name containing only letters and underscores can be accessed via a dot selector.
54
55- [foo.bar > 0](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedParameter)
56
57### Custom Selector
58
59Parameter names like `response-time` will be interpreted as `response` minus `time`. While gval doesn't support these parameter names directly, you can easily access them via a custom extension like [JSON Path](https://github.com/PaesslerAG/jsonpath):
60
61- [$["response-time"]](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Jsonpath)
62
63Jsonpath is also suitable for accessing array elements.
64
65### Fields and Methods
66
67If you have structs in your parameters, you can access their fields and methods in the usual way:
68
69- [foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-FlatAccessor)
70
71It also works if the parameter is a struct directly
72[Hello + World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-Accessor)
73or if the fields are nested
74[foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Evaluate-NestedAccessor)
75
76This may be convenient but note that using accessors on strucs makes the expression about four times slower than just using a parameter (consult the benchmarks for more precise measurements on your system). If there are functions you want to use, it's faster (and probably cleaner) to define them as functions (see the Evaluate section). These approaches use no reflection, and are designed to be fast and clean.
77
78## Default Language
79
80The default language is in serveral sub languages like text, arithmetic or propositional logic defined. See [Godoc](https://pkg.go.dev/github.com/PaesslerAG/gval/#Gval) for details. All sub languages are merged into gval.Full which contains the following elements:
81
82- Modifiers: `+` `-` `/` `*` `&` `|` `^` `**` `%` `>>` `<<`
83- Comparators: `>` `>=` `<` `<=` `==` `!=` `=~` `!~`
84- Logical ops: `||` `&&`
85- Numeric constants, as 64-bit floating point (`12345.678`)
86- String constants (double quotes: `"foobar"`)
87- Date function 'Date(x)', using any permutation of RFC3339, ISO8601, ruby date, or unix date
88- Boolean constants: `true` `false`
89- Parentheses to control order of evaluation `(` `)`
90- Json Arrays : `[1, 2, "foo"]`
91- Json Objects : `{"a":1, "b":2, "c":"foo"}`
92- Prefixes: `!` `-` `~`
93- Ternary conditional: `?` `:`
94- Null coalescence: `??`
95
96## Customize
97
98Gval is completly customizable. Every constant, function or operator can be defined separately and existing expression languages can be reused:
99
100- [foo.Hello + foo.World()](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-Language)
101
102For details see [Godoc](https://pkg.go.dev/github.com/PaesslerAG/gval).
103
104### Implementing custom selector
105
106In a case you want to provide custom logic for selectors you can implement `SelectGVal(ctx context.Context, k string) (interface{}, error)` on your struct.
107Function receives next part of the path and can return any type of var that is again evaluated through standard gval procedures.
108
109[Example Custom Selector](https://pkg.go.dev/github.com/PaesslerAG/gval/#example-custom-selector)
110
111### External gval Languages
112
113A list of external libraries for gval. Feel free to add your own library.
114
115- [gvalstrings](https://github.com/generikvault/gvalstrings) parse single quoted strings in gval.
116- [jsonpath](https://github.com/PaesslerAG/jsonpath) full support for jsonpath in gval.
117
118## Performance
119
120The library is built with the intention of being quick but has not been aggressively profiled and optimized. For most applications, though, it is completely fine.
121If performance is an issue, make sure to create your expression language with all functions, constants and operators only once. Evaluating an expression like gval.Evaluate("expression, const1, func1, func2, ...) creates a new gval.Language everytime it is called and slows execution.
122
123The library comes with a bunch of benchmarks to measure the performance of parsing and evaluating expressions. You can run them with `go test -bench=.`.
124
125For a very rough idea of performance, here are the results from a benchmark run on a Dell Latitude E7470 Win 10 i5-6300U.
126
127``` text
128BenchmarkGval/const_evaluation-4 500000000 3.57 ns/op
129BenchmarkGval/const_parsing-4 1000000 1144 ns/op
130BenchmarkGval/single_parameter_evaluation-4 10000000 165 ns/op
131BenchmarkGval/single_parameter_parsing-4 1000000 1648 ns/op
132BenchmarkGval/parameter_evaluation-4 5000000 352 ns/op
133BenchmarkGval/parameter_parsing-4 500000 2773 ns/op
134BenchmarkGval/common_evaluation-4 3000000 434 ns/op
135BenchmarkGval/common_parsing-4 300000 4419 ns/op
136BenchmarkGval/complex_evaluation-4 100000000 11.6 ns/op
137BenchmarkGval/complex_parsing-4 100000 17936 ns/op
138BenchmarkGval/literal_evaluation-4 300000000 3.84 ns/op
139BenchmarkGval/literal_parsing-4 500000 2559 ns/op
140BenchmarkGval/modifier_evaluation-4 500000000 3.54 ns/op
141BenchmarkGval/modifier_parsing-4 500000 3755 ns/op
142BenchmarkGval/regex_evaluation-4 50000 21347 ns/op
143BenchmarkGval/regex_parsing-4 200000 6480 ns/op
144BenchmarkGval/constant_regex_evaluation-4 1000000 1000 ns/op
145BenchmarkGval/constant_regex_parsing-4 200000 9417 ns/op
146BenchmarkGval/accessors_evaluation-4 3000000 417 ns/op
147BenchmarkGval/accessors_parsing-4 1000000 1778 ns/op
148BenchmarkGval/accessors_method_evaluation-4 1000000 1931 ns/op
149BenchmarkGval/accessors_method_parsing-4 1000000 1729 ns/op
150BenchmarkGval/accessors_method_parameter_evaluation-4 1000000 2162 ns/op
151BenchmarkGval/accessors_method_parameter_parsing-4 500000 2618 ns/op
152BenchmarkGval/nested_accessors_evaluation-4 2000000 681 ns/op
153BenchmarkGval/nested_accessors_parsing-4 1000000 2115 ns/op
154BenchmarkRandom-4 500000 3631 ns/op
155ok
156```
157
158## API Breaks
159
160Gval is designed with easy expandability in mind and API breaks will be avoided if possible. If API breaks are unavoidable they wil be explicitly stated via an increased major version number.
161
162-------------------------------------
163Credits to Reene French for the gophers.
View as plain text