add Eval function (#338)

This commit is contained in:
daniel 2021-07-05 01:49:38 -07:00 committed by GitHub
parent 50af716288
commit ac805806f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 114 additions and 10 deletions

View file

@ -4,7 +4,7 @@
# The Tengo Language
[![GoDoc](https://godoc.org/github.com/d5/tengo?status.svg)](https://godoc.org/github.com/d5/tengo)
[![GoDoc](https://godoc.org/github.com/d5/tengo/v2?status.svg)](https://godoc.org/github.com/d5/tengo/v2)
![test](https://github.com/d5/tengo/workflows/test/badge.svg)
[![Go Report Card](https://goreportcard.com/badge/github.com/d5/tengo)](https://goreportcard.com/report/github.com/d5/tengo)
@ -93,9 +93,9 @@ import (
)
func main() {
// Tengo script code
src := `
each := func(seq, fn) {
// create a new Script instance
script := tengo.NewScript([]byte(
`each := func(seq, fn) {
for x in seq { fn(x) }
}
@ -104,10 +104,7 @@ mul := 1
each([a, b, c, d], func(x) {
sum += x
mul *= x
})`
// create a new Script instance
script := tengo.NewScript([]byte(src))
})`))
// set values
_ = script.Add("a", 1)
@ -128,6 +125,19 @@ each([a, b, c, d], func(x) {
}
```
Or, if you need to evaluate a simple expression, you can use [Eval](https://pkg.go.dev/github.com/d5/tengo/v2#Eval) function instead:
```golang
res, err := tengo.Eval(ctx,
`input ? "success" : "fail"`,
map[string]interface{}{"input": 1})
if err != nil {
panic(err)
}
fmt.Println(res) // "success"
```
## References
- [Language Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md)

35
eval.go Normal file
View file

@ -0,0 +1,35 @@
package tengo
import (
"context"
"fmt"
"strings"
)
// Eval compiles and executes given expr with params, and returns an
// evaluated value. expr must be an expression. Otherwise it will fail to
// compile. Expression must not use or define variable "__res__" as it's
// reserved for the internal usage.
func Eval(
ctx context.Context,
expr string,
params map[string]interface{},
) (interface{}, error) {
expr = strings.TrimSpace(expr)
if expr == "" {
return nil, fmt.Errorf("empty expression")
}
script := NewScript([]byte(fmt.Sprintf("__res__ := (%s)", expr)))
for pk, pv := range params {
err := script.Add(pk, pv)
if err != nil {
return nil, fmt.Errorf("script add: %w", err)
}
}
compiled, err := script.RunContext(ctx)
if err != nil {
return nil, fmt.Errorf("script run: %w", err)
}
return compiled.Get("__res__").Value(), nil
}

59
eval_test.go Normal file
View file

@ -0,0 +1,59 @@
package tengo_test
import (
"context"
"testing"
"github.com/d5/tengo/v2"
"github.com/d5/tengo/v2/require"
)
func TestEval(t *testing.T) {
eval := func(
expr string,
params map[string]interface{},
expected interface{},
) {
ctx := context.Background()
actual, err := tengo.Eval(ctx, expr, params)
require.NoError(t, err)
require.Equal(t, expected, actual)
}
eval(`undefined`, nil, nil)
eval(`1`, nil, int64(1))
eval(`19 + 23`, nil, int64(42))
eval(`"foo bar"`, nil, "foo bar")
eval(`[1, 2, 3][1]`, nil, int64(2))
eval(
`5 + p`,
map[string]interface{}{
"p": 7,
},
int64(12),
)
eval(
`"seven is " + p`,
map[string]interface{}{
"p": 7,
},
"seven is 7",
)
eval(
`"" + a + b`,
map[string]interface{}{
"a": 7,
"b": " is seven",
},
"7 is seven",
)
eval(
`a ? "success" : "fail"`,
map[string]interface{}{
"a": 1,
},
"success",
)
}