Skip to content

Commit

Permalink
add Eval function (#338)
Browse files Browse the repository at this point in the history
  • Loading branch information
d5 authored Jul 5, 2021
1 parent 50af716 commit ac80580
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 10 deletions.
30 changes: 20 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -93,21 +93,18 @@ 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) }
}
sum := 0
mul := 1
each([a, b, c, d], func(x) {
sum += x
mul *= x
})`

// create a new Script instance
script := tengo.NewScript([]byte(src))
sum += x
mul *= x
})`))

// set values
_ = script.Add("a", 1)
Expand All @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions eval.go
Original file line number Diff line number Diff line change
@@ -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 changes: 59 additions & 0 deletions eval_test.go
Original file line number Diff line number Diff line change
@@ -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",
)
}

0 comments on commit ac80580

Please sign in to comment.