diff --git a/go.mod b/go.mod index b9e42e0014e2f82e8f42c24a8f1dd7096831cfd8..10fb6ce868def0381d115bc504c7b7542eafcb77 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( github.com/coreos/go-systemd/v22 v22.3.2 - github.com/d5/tengo/v2 v2.7.0 + github.com/d5/tengo/v2 v2.10.0 github.com/go-kit/kit v0.10.0 // indirect github.com/golang-migrate/migrate/v4 v4.14.1 github.com/golang/protobuf v1.5.2 diff --git a/go.sum b/go.sum index 07067a72acb4b327e6a4d94cb81b4c1b6e1e6965..20030d8d3cf11ac793e165d855a2093abc759102 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/d5/tengo/v2 v2.7.0 h1:oAGQ+gcas0/T0bdqvNxfKzjOJhpQRwceWIF5gldB3aM= github.com/d5/tengo/v2 v2.7.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= +github.com/d5/tengo/v2 v2.10.0 h1:gR3VwfJDBlffV8WzfSNNJ7WJtWduwbTKlAu14cA2fRs= +github.com/d5/tengo/v2 v2.10.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/vendor/github.com/d5/tengo/v2/README.md b/vendor/github.com/d5/tengo/v2/README.md index bee195999597cb3d8d4bd3dfdefa304d08398045..c19c5699173c2c81dc70e1c40eab3dd3d3e3f553 100644 --- a/vendor/github.com/d5/tengo/v2/README.md +++ b/vendor/github.com/d5/tengo/v2/README.md @@ -1,10 +1,6 @@ -<p align="center"> - <img src="https://raw.githubusercontent.com/d5/tengolang-share/master/logo_400.png" width="200" height="200"> -</p> - # The Tengo Language -[](https://godoc.org/github.com/d5/tengo) +[](https://godoc.org/github.com/d5/tengo/v2)  [](https://goreportcard.com/report/github.com/d5/tengo) @@ -53,19 +49,19 @@ fmt.println(sum("", [1, 2, 3])) // "123" | | fib(35) | fibt(35) | Language (Type) | | :--- | ---: | ---: | :---: | -| [**Tengo**](https://github.com/d5/tengo) | `2,931ms` | `4ms` | Tengo (VM) | -| [go-lua](https://github.com/Shopify/go-lua) | `4,824ms` | `4ms` | Lua (VM) | -| [GopherLua](https://github.com/yuin/gopher-lua) | `5,365ms` | `4ms` | Lua (VM) | -| [goja](https://github.com/dop251/goja) | `5,533ms` | `5ms` | JavaScript (VM) | -| [starlark-go](https://github.com/google/starlark-go) | `11,495ms` | `5ms` | Starlark (Interpreter) | -| [Yaegi](https://github.com/containous/yaegi) | `15,645ms` | `12ms` | Yaegi (Interpreter) | -| [gpython](https://github.com/go-python/gpython) | `16,322ms` | `5ms` | Python (Interpreter) | -| [otto](https://github.com/robertkrimen/otto) | `73,093ms` | `10ms` | JavaScript (Interpreter) | -| [Anko](https://github.com/mattn/anko) | `79,809ms` | `8ms` | Anko (Interpreter) | +| [**Tengo**](https://github.com/d5/tengo) | `2,315ms` | `3ms` | Tengo (VM) | +| [go-lua](https://github.com/Shopify/go-lua) | `4,028ms` | `3ms` | Lua (VM) | +| [GopherLua](https://github.com/yuin/gopher-lua) | `4,409ms` | `3ms` | Lua (VM) | +| [goja](https://github.com/dop251/goja) | `5,194ms` | `4ms` | JavaScript (VM) | +| [starlark-go](https://github.com/google/starlark-go) | `6,954ms` | `3ms` | Starlark (Interpreter) | +| [gpython](https://github.com/go-python/gpython) | `11,324ms` | `4ms` | Python (Interpreter) | +| [Yaegi](https://github.com/containous/yaegi) | `11,715ms` | `10ms` | Yaegi (Interpreter) | +| [otto](https://github.com/robertkrimen/otto) | `48,539ms` | `6ms` | JavaScript (Interpreter) | +| [Anko](https://github.com/mattn/anko) | `52,821ms` | `6ms` | Anko (Interpreter) | | - | - | - | - | -| Go | `53ms` | `3ms` | Go (Native) | -| Lua | `1,612ms` | `3ms` | Lua (Native) | -| Python | `2,632ms` | `23ms` | Python 2 (Native) | +| Go | `47ms` | `2ms` | Go (Native) | +| Lua | `756ms` | `2ms` | Lua (Native) | +| Python | `1,907ms` | `14ms` | Python2 (Native) | _* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): Fibonacci(35)_ @@ -93,21 +89,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) @@ -128,6 +121,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) diff --git a/vendor/github.com/d5/tengo/v2/compiler.go b/vendor/github.com/d5/tengo/v2/compiler.go index 53cc7d38027af58249f19aaab38d99e1f861edf9..e4e04303b1e01b60b7588d53796293aa33b112db 100644 --- a/vendor/github.com/d5/tengo/v2/compiler.go +++ b/vendor/github.com/d5/tengo/v2/compiler.go @@ -1,9 +1,11 @@ package tengo import ( + "errors" "fmt" "io" "io/ioutil" + "os" "path/filepath" "reflect" "strings" @@ -45,11 +47,12 @@ type Compiler struct { parent *Compiler modulePath string importDir string + importFileExt []string constants []Object symbolTable *SymbolTable scopes []compilationScope scopeIndex int - modules *ModuleMap + modules ModuleGetter compiledModules map[string]*CompiledFunction allowFileImport bool loops []*loop @@ -63,7 +66,7 @@ func NewCompiler( file *parser.SourceFile, symbolTable *SymbolTable, constants []Object, - modules *ModuleMap, + modules ModuleGetter, trace io.Writer, ) *Compiler { mainScope := compilationScope{ @@ -96,6 +99,7 @@ func NewCompiler( trace: trace, modules: modules, compiledModules: make(map[string]*CompiledFunction), + importFileExt: []string{SourceFileExtDefault}, } } @@ -538,12 +542,8 @@ func (c *Compiler) Compile(node parser.Node) error { } } else if c.allowFileImport { moduleName := node.ModuleName - if !strings.HasSuffix(moduleName, ".tengo") { - moduleName += ".tengo" - } - modulePath, err := filepath.Abs( - filepath.Join(c.importDir, moduleName)) + modulePath, err := c.getPathModule(moduleName) if err != nil { return c.errorf(node, "module file path error: %s", err.Error()) @@ -640,6 +640,39 @@ func (c *Compiler) SetImportDir(dir string) { c.importDir = dir } +// SetImportFileExt sets the extension name of the source file for loading +// local module files. +// +// Use this method if you want other source file extension than ".tengo". +// +// // this will search for *.tengo, *.foo, *.bar +// err := c.SetImportFileExt(".tengo", ".foo", ".bar") +// +// This function requires at least one argument, since it will replace the +// current list of extension name. +func (c *Compiler) SetImportFileExt(exts ...string) error { + if len(exts) == 0 { + return fmt.Errorf("missing arg: at least one argument is required") + } + + for _, ext := range exts { + if ext != filepath.Ext(ext) || ext == "" { + return fmt.Errorf("invalid file extension: %s", ext) + } + } + + c.importFileExt = exts // Replace the hole current extension list + + return nil +} + +// GetImportFileExt returns the current list of extension name. +// Thease are the complementary suffix of the source file to search and load +// local module files. +func (c *Compiler) GetImportFileExt() []string { + return c.importFileExt +} + func (c *Compiler) compileAssign( node parser.Node, lhs, rhs []parser.Expr, @@ -1098,6 +1131,7 @@ func (c *Compiler) fork( child.parent = c // parent to set to current compiler child.allowFileImport = c.allowFileImport child.importDir = c.importDir + child.importFileExt = c.importFileExt if isFile && c.importDir != "" { child.importDir = filepath.Dir(modulePath) } @@ -1287,6 +1321,28 @@ func (c *Compiler) printTrace(a ...interface{}) { _, _ = fmt.Fprintln(c.trace, a...) } +func (c *Compiler) getPathModule(moduleName string) (pathFile string, err error) { + for _, ext := range c.importFileExt { + nameFile := moduleName + + if !strings.HasSuffix(nameFile, ext) { + nameFile += ext + } + + pathFile, err = filepath.Abs(filepath.Join(c.importDir, nameFile)) + if err != nil { + continue + } + + // Check if file exists + if _, err := os.Stat(pathFile); !errors.Is(err, os.ErrNotExist) { + return pathFile, nil + } + } + + return "", fmt.Errorf("module '%s' not found at: %s", moduleName, pathFile) +} + func resolveAssignLHS( expr parser.Expr, ) (name string, selectors []parser.Expr) { diff --git a/vendor/github.com/d5/tengo/v2/eval.go b/vendor/github.com/d5/tengo/v2/eval.go new file mode 100644 index 0000000000000000000000000000000000000000..9ce9b2a7332365f1761282e3a21246ddec0122ee --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/eval.go @@ -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 +} diff --git a/vendor/github.com/d5/tengo/v2/modules.go b/vendor/github.com/d5/tengo/v2/modules.go index c8fcde7fd1386e691fd1c891281e0b8a0a5d36eb..dadd5a3b78fc614e92f80a2a46bb14342970a322 100644 --- a/vendor/github.com/d5/tengo/v2/modules.go +++ b/vendor/github.com/d5/tengo/v2/modules.go @@ -6,6 +6,11 @@ type Importable interface { Import(moduleName string) (interface{}, error) } +// ModuleGetter enables implementing dynamic module loading. +type ModuleGetter interface { + Get(name string) Importable +} + // ModuleMap represents a set of named modules. Use NewModuleMap to create a // new module map. type ModuleMap struct { diff --git a/vendor/github.com/d5/tengo/v2/script.go b/vendor/github.com/d5/tengo/v2/script.go index 46e48029b6901dcaf4951414ad854135eac010c6..82b02f524bf78dc9c2849c571a6be2475a128c92 100644 --- a/vendor/github.com/d5/tengo/v2/script.go +++ b/vendor/github.com/d5/tengo/v2/script.go @@ -12,7 +12,7 @@ import ( // Script can simplify compilation and execution of embedded scripts. type Script struct { variables map[string]*Variable - modules *ModuleMap + modules ModuleGetter input []byte maxAllocs int64 maxConstObjects int @@ -54,7 +54,7 @@ func (s *Script) Remove(name string) bool { } // SetImports sets import modules. -func (s *Script) SetImports(modules *ModuleMap) { +func (s *Script) SetImports(modules ModuleGetter) { s.modules = modules } @@ -219,6 +219,18 @@ func (c *Compiled) RunContext(ctx context.Context) (err error) { v := NewVM(c.bytecode, c.globals, c.maxAllocs) ch := make(chan error, 1) go func() { + defer func() { + if r := recover(); r != nil { + switch e := r.(type) { + case string: + ch <- fmt.Errorf(e) + case error: + ch <- e + default: + ch <- fmt.Errorf("unknown panic: %v", e) + } + } + }() ch <- v.Run() }() diff --git a/vendor/github.com/d5/tengo/v2/tengo.go b/vendor/github.com/d5/tengo/v2/tengo.go index 098a19701b50d05bdad9961f2f01906017749aa4..490e9aed80f239478c401198f04215af784ddc1e 100644 --- a/vendor/github.com/d5/tengo/v2/tengo.go +++ b/vendor/github.com/d5/tengo/v2/tengo.go @@ -26,6 +26,9 @@ const ( // MaxFrames is the maximum number of function frames for a VM. MaxFrames = 1024 + + // SourceFileExtDefault is the default extension for source files. + SourceFileExtDefault = ".tengo" ) // CallableFunc is a function signature for the callable functions. diff --git a/vendor/github.com/d5/tengo/v2/vm.go b/vendor/github.com/d5/tengo/v2/vm.go index 811ecef90eab21f3bd513e323529a7bc44fa2e1f..c8365252a7d743533cbea674166403f35f3fc6a1 100644 --- a/vendor/github.com/d5/tengo/v2/vm.go +++ b/vendor/github.com/d5/tengo/v2/vm.go @@ -293,7 +293,7 @@ func (v *VM) run() { case parser.OpMap: v.ip += 2 numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - kv := make(map[string]Object) + kv := make(map[string]Object, numElements) for i := v.sp - numElements; i < v.sp; i += 2 { key := v.stack[i] value := v.stack[i+1] diff --git a/vendor/modules.txt b/vendor/modules.txt index d27039d7f77a5b02dee3149f3b6296eb4bf15b5d..88670389682b1b1f8aa6f7c5fd1f12f426ffb0a3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -5,7 +5,7 @@ github.com/cespare/xxhash/v2 # github.com/coreos/go-systemd/v22 v22.3.2 ## explicit github.com/coreos/go-systemd/v22/daemon -# github.com/d5/tengo/v2 v2.7.0 +# github.com/d5/tengo/v2 v2.10.0 ## explicit github.com/d5/tengo/v2 github.com/d5/tengo/v2/parser