summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWim <wim@42.be>2019-04-06 22:18:25 +0200
committerGitHub <noreply@github.com>2019-04-06 22:18:25 +0200
commit115d20373c21b107a428a55247c64f900e116038 (patch)
treec7299b3d3be2a48c0f2d5bfbd856cbd1b27d2e55
parentcdf33e5748c110e12097130bdb44637e3d14b229 (diff)
downloadmatterbridge-msglm-115d20373c21b107a428a55247c64f900e116038.tar.gz
matterbridge-msglm-115d20373c21b107a428a55247c64f900e116038.tar.bz2
matterbridge-msglm-115d20373c21b107a428a55247c64f900e116038.zip
Update tengo vendor and load the stdlib. Fixes #789 (#792)
-rw-r--r--gateway/gateway.go2
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--vendor/github.com/d5/tengo/Makefile5
-rw-r--r--vendor/github.com/d5/tengo/README.md32
-rw-r--r--vendor/github.com/d5/tengo/compiler/bytecode.go66
-rw-r--r--vendor/github.com/d5/tengo/compiler/bytecode_decode.go97
-rw-r--r--vendor/github.com/d5/tengo/compiler/bytecode_optimize.go129
-rw-r--r--vendor/github.com/d5/tengo/compiler/compilation_scope.go7
-rw-r--r--vendor/github.com/d5/tengo/compiler/compiler.go268
-rw-r--r--vendor/github.com/d5/tengo/compiler/compiler_assign.go22
-rw-r--r--vendor/github.com/d5/tengo/compiler/compiler_module.go87
-rw-r--r--vendor/github.com/d5/tengo/compiler/instructions.go13
-rw-r--r--vendor/github.com/d5/tengo/compiler/opcodes.go282
-rw-r--r--vendor/github.com/d5/tengo/compiler/symbol_table.go4
-rw-r--r--vendor/github.com/d5/tengo/objects/break.go37
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_json.go60
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_module.go23
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_print.go83
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_type_checks.go12
-rw-r--r--vendor/github.com/d5/tengo/objects/builtins.go60
-rw-r--r--vendor/github.com/d5/tengo/objects/bytes.go8
-rw-r--r--vendor/github.com/d5/tengo/objects/bytes_iterator.go57
-rw-r--r--vendor/github.com/d5/tengo/objects/closure.go4
-rw-r--r--vendor/github.com/d5/tengo/objects/compiled_function.go11
-rw-r--r--vendor/github.com/d5/tengo/objects/continue.go38
-rw-r--r--vendor/github.com/d5/tengo/objects/conversion.go25
-rw-r--r--vendor/github.com/d5/tengo/objects/count_objects.go31
-rw-r--r--vendor/github.com/d5/tengo/objects/importable.go7
-rw-r--r--vendor/github.com/d5/tengo/objects/module_map.go77
-rw-r--r--vendor/github.com/d5/tengo/objects/object_ptr.go41
-rw-r--r--vendor/github.com/d5/tengo/objects/return_value.go39
-rw-r--r--vendor/github.com/d5/tengo/objects/source_module.go11
-rw-r--r--vendor/github.com/d5/tengo/objects/user_function.go5
-rw-r--r--vendor/github.com/d5/tengo/runtime/errors.go3
-rw-r--r--vendor/github.com/d5/tengo/runtime/frame.go2
-rw-r--r--vendor/github.com/d5/tengo/runtime/vm.go758
-rw-r--r--vendor/github.com/d5/tengo/script/compiled.go102
-rw-r--r--vendor/github.com/d5/tengo/script/conversion.go33
-rw-r--r--vendor/github.com/d5/tengo/script/script.go125
-rw-r--r--vendor/github.com/d5/tengo/script/variable.go36
-rw-r--r--vendor/github.com/d5/tengo/stdlib/builtin_modules.go14
-rw-r--r--vendor/github.com/d5/tengo/stdlib/errors.go11
-rw-r--r--vendor/github.com/d5/tengo/stdlib/fmt.go116
-rw-r--r--vendor/github.com/d5/tengo/stdlib/func_typedefs.go1125
-rw-r--r--vendor/github.com/d5/tengo/stdlib/gensrcmods.go53
-rw-r--r--vendor/github.com/d5/tengo/stdlib/json.go126
-rw-r--r--vendor/github.com/d5/tengo/stdlib/json/decode.go374
-rw-r--r--vendor/github.com/d5/tengo/stdlib/json/encode.go147
-rw-r--r--vendor/github.com/d5/tengo/stdlib/json/scanner.go559
-rw-r--r--vendor/github.com/d5/tengo/stdlib/math.go74
-rw-r--r--vendor/github.com/d5/tengo/stdlib/os.go492
-rw-r--r--vendor/github.com/d5/tengo/stdlib/os_exec.go113
-rw-r--r--vendor/github.com/d5/tengo/stdlib/os_file.go96
-rw-r--r--vendor/github.com/d5/tengo/stdlib/os_process.go62
-rw-r--r--vendor/github.com/d5/tengo/stdlib/rand.go102
-rw-r--r--vendor/github.com/d5/tengo/stdlib/source_modules.go8
-rw-r--r--vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo128
-rw-r--r--vendor/github.com/d5/tengo/stdlib/stdlib.go34
-rw-r--r--vendor/github.com/d5/tengo/stdlib/text.go737
-rw-r--r--vendor/github.com/d5/tengo/stdlib/text_regexp.go228
-rw-r--r--vendor/github.com/d5/tengo/stdlib/times.go989
-rw-r--r--vendor/modules.txt4
63 files changed, 7008 insertions, 1292 deletions
diff --git a/gateway/gateway.go b/gateway/gateway.go
index 82be2a98..9957a21d 100644
--- a/gateway/gateway.go
+++ b/gateway/gateway.go
@@ -10,6 +10,7 @@ import (
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/d5/tengo/script"
+ "github.com/d5/tengo/stdlib"
lru "github.com/hashicorp/golang-lru"
"github.com/peterhellberg/emojilib"
"github.com/sirupsen/logrus"
@@ -503,6 +504,7 @@ func modifyMessageTengo(filename string, msg *config.Message) error {
return err
}
s := script.New(res)
+ s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
_ = s.Add("msgText", msg.Text)
_ = s.Add("msgUsername", msg.Username)
_ = s.Add("msgAccount", msg.Account)
diff --git a/go.mod b/go.mod
index 273ef6f0..a3b47b53 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
github.com/Jeffail/gabs v1.1.1 // indirect
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
github.com/bwmarrin/discordgo v0.19.0
- github.com/d5/tengo v1.12.1
+ github.com/d5/tengo v1.20.0
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
github.com/fsnotify/fsnotify v1.4.7
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
diff --git a/go.sum b/go.sum
index c14505b2..ea77656b 100644
--- a/go.sum
+++ b/go.sum
@@ -15,8 +15,8 @@ github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVO
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/d5/tengo v1.12.1 h1:libKkDM95CsZgYs6E5eiEaM9sbcw2EzJRSkr9o5NO4s=
-github.com/d5/tengo v1.12.1/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY=
+github.com/d5/tengo v1.20.0 h1:lFmktzEGR6khlZu2MHUWJ5oDWS4l3jNRV/OhclZgcYc=
+github.com/d5/tengo v1.20.0/go.mod h1:gsbjo7lBXzBIWBd6NQp1lRKqqiDDANqBOyhW8rTlFsY=
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/Makefile b/vendor/github.com/d5/tengo/Makefile
index e4613390..99daafd1 100644
--- a/vendor/github.com/d5/tengo/Makefile
+++ b/vendor/github.com/d5/tengo/Makefile
@@ -1,10 +1,13 @@
vet:
go vet ./...
+generate:
+ go generate ./...
+
lint:
golint -set_exit_status ./...
-test: vet lint
+test: generate vet lint
go test -race -cover ./...
fmt:
diff --git a/vendor/github.com/d5/tengo/README.md b/vendor/github.com/d5/tengo/README.md
index 7aa05966..e68b0f86 100644
--- a/vendor/github.com/d5/tengo/README.md
+++ b/vendor/github.com/d5/tengo/README.md
@@ -7,7 +7,6 @@
[![GoDoc](https://godoc.org/github.com/d5/tengo?status.svg)](https://godoc.org/github.com/d5/tengo/script)
[![Go Report Card](https://goreportcard.com/badge/github.com/d5/tengo)](https://goreportcard.com/report/github.com/d5/tengo)
[![Build Status](https://travis-ci.org/d5/tengo.svg?branch=master)](https://travis-ci.org/d5/tengo)
-[![](https://img.shields.io/badge/Support%20Tengo-%241-brightgreen.svg)](https://www.patreon.com/tengolang)
**Tengo is a small, dynamic, fast, secure script language for Go.**
@@ -16,6 +15,8 @@ Tengo is **[fast](#benchmark)** and secure because it's compiled/executed as byt
```golang
/* The Tengo Language */
+fmt := import("fmt")
+
each := func(seq, fn) {
for x in seq { fn(x) }
}
@@ -25,11 +26,11 @@ sum := func(init, seq) {
return init
}
-n := sum(0, [1, 2, 3]) // == 6
-s := sum("", [1, 2, 3]) // == "123"
+fmt.println(sum(0, [1, 2, 3])) // "6"
+fmt.println(sum("", [1, 2, 3])) // "123"
```
-> Run this code in the [Playground](https://tengolang.com/?s=d01cf9ed81daba939e26618530eb171f7397d9c9)
+> Run this code in the [Playground](https://tengolang.com/?s=0c8d5d0d88f2795a7093d7f35ae12c3afa17bea3)
## Features
@@ -41,22 +42,23 @@ s := sum("", [1, 2, 3]) // == "123"
- [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md)
- Compiler/runtime written in native Go _(no external deps or cgo)_
- Executable as a [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) language / REPL
+- Use cases: rules engine, [state machine](https://github.com/d5/go-fsm), [gaming](https://github.com/d5/pbr), data pipeline, [transpiler](https://github.com/d5/tengo2lua)
## Benchmark
| | fib(35) | fibt(35) | Type |
| :--- | ---: | ---: | :---: |
-| Go | `58ms` | `4ms` | Go (native) |
-| [**Tengo**](https://github.com/d5/tengo) | `4,180ms` | `5ms` | VM on Go |
-| Lua | `1,695ms` | `3ms` | Lua (native) |
-| [go-lua](https://github.com/Shopify/go-lua) | `5,163ms` | `5ms` | Lua VM on Go |
-| [GopherLua](https://github.com/yuin/gopher-lua) | `5,525ms` | `5ms` | Lua VM on Go |
-| Python | `3,097ms` | `27ms` | Python (native) |
-| [starlark-go](https://github.com/google/starlark-go) | `15,307ms` | `5ms` | Python-like Interpreter on Go |
-| [gpython](https://github.com/go-python/gpython) | `17,656ms` | `5ms` | Python Interpreter on Go |
-| [goja](https://github.com/dop251/goja) | `6,876ms` | `5ms` | JS VM on Go |
-| [otto](https://github.com/robertkrimen/otto) | `81,886ms` | `12ms` | JS Interpreter on Go |
-| [Anko](https://github.com/mattn/anko) | `97,517ms` | `14ms` | Interpreter on Go |
+| Go | `48ms` | `3ms` | Go (native) |
+| [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go |
+| Lua | `1,416ms` | `3ms` | Lua (native) |
+| [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go |
+| [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go |
+| Python | `2,588ms` | `26ms` | Python (native) |
+| [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go |
+| [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go |
+| [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go |
+| [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go |
+| [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go |
_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): Fibonacci(35)_
_* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): [tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_
diff --git a/vendor/github.com/d5/tengo/compiler/bytecode.go b/vendor/github.com/d5/tengo/compiler/bytecode.go
index 42527731..35f36c0a 100644
--- a/vendor/github.com/d5/tengo/compiler/bytecode.go
+++ b/vendor/github.com/d5/tengo/compiler/bytecode.go
@@ -17,32 +17,6 @@ type Bytecode struct {
Constants []objects.Object
}
-// Decode reads Bytecode data from the reader.
-func (b *Bytecode) Decode(r io.Reader) error {
- dec := gob.NewDecoder(r)
-
- if err := dec.Decode(&b.FileSet); err != nil {
- return err
- }
- // TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet
- // as it's private field and not serialized by gob encoder/decoder.
-
- if err := dec.Decode(&b.MainFunction); err != nil {
- return err
- }
-
- if err := dec.Decode(&b.Constants); err != nil {
- return err
- }
-
- // replace Bool and Undefined with known value
- for i, v := range b.Constants {
- b.Constants[i] = cleanupObjects(v)
- }
-
- return nil
-}
-
// Encode writes Bytecode data to the writer.
func (b *Bytecode) Encode(w io.Writer) error {
enc := gob.NewEncoder(w)
@@ -59,6 +33,17 @@ func (b *Bytecode) Encode(w io.Writer) error {
return enc.Encode(b.Constants)
}
+// CountObjects returns the number of objects found in Constants.
+func (b *Bytecode) CountObjects() int {
+ n := 0
+
+ for _, c := range b.Constants {
+ n += objects.CountObjects(c)
+ }
+
+ return n
+}
+
// FormatInstructions returns human readable string representations of
// compiled instructions.
func (b *Bytecode) FormatInstructions() []string {
@@ -83,51 +68,22 @@ func (b *Bytecode) FormatConstants() (output []string) {
return
}
-func cleanupObjects(o objects.Object) objects.Object {
- switch o := o.(type) {
- case *objects.Bool:
- if o.IsFalsy() {
- return objects.FalseValue
- }
- return objects.TrueValue
- case *objects.Undefined:
- return objects.UndefinedValue
- case *objects.Array:
- for i, v := range o.Value {
- o.Value[i] = cleanupObjects(v)
- }
- case *objects.Map:
- for k, v := range o.Value {
- o.Value[k] = cleanupObjects(v)
- }
- }
-
- return o
-}
-
func init() {
gob.Register(&source.FileSet{})
gob.Register(&source.File{})
gob.Register(&objects.Array{})
- gob.Register(&objects.ArrayIterator{})
gob.Register(&objects.Bool{})
- gob.Register(&objects.Break{})
- gob.Register(&objects.BuiltinFunction{})
gob.Register(&objects.Bytes{})
gob.Register(&objects.Char{})
gob.Register(&objects.Closure{})
gob.Register(&objects.CompiledFunction{})
- gob.Register(&objects.Continue{})
gob.Register(&objects.Error{})
gob.Register(&objects.Float{})
gob.Register(&objects.ImmutableArray{})
gob.Register(&objects.ImmutableMap{})
gob.Register(&objects.Int{})
gob.Register(&objects.Map{})
- gob.Register(&objects.MapIterator{})
- gob.Register(&objects.ReturnValue{})
gob.Register(&objects.String{})
- gob.Register(&objects.StringIterator{})
gob.Register(&objects.Time{})
gob.Register(&objects.Undefined{})
gob.Register(&objects.UserFunction{})
diff --git a/vendor/github.com/d5/tengo/compiler/bytecode_decode.go b/vendor/github.com/d5/tengo/compiler/bytecode_decode.go
new file mode 100644
index 00000000..b3b32a64
--- /dev/null
+++ b/vendor/github.com/d5/tengo/compiler/bytecode_decode.go
@@ -0,0 +1,97 @@
+package compiler
+
+import (
+ "encoding/gob"
+ "fmt"
+ "io"
+
+ "github.com/d5/tengo/objects"
+)
+
+// Decode reads Bytecode data from the reader.
+func (b *Bytecode) Decode(r io.Reader, modules *objects.ModuleMap) error {
+ if modules == nil {
+ modules = objects.NewModuleMap()
+ }
+
+ dec := gob.NewDecoder(r)
+
+ if err := dec.Decode(&b.FileSet); err != nil {
+ return err
+ }
+ // TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet
+ // as it's private field and not serialized by gob encoder/decoder.
+
+ if err := dec.Decode(&b.MainFunction); err != nil {
+ return err
+ }
+
+ if err := dec.Decode(&b.Constants); err != nil {
+ return err
+ }
+ for i, v := range b.Constants {
+ fv, err := fixDecoded(v, modules)
+ if err != nil {
+ return err
+ }
+ b.Constants[i] = fv
+ }
+
+ return nil
+}
+
+func fixDecoded(o objects.Object, modules *objects.ModuleMap) (objects.Object, error) {
+ switch o := o.(type) {
+ case *objects.Bool:
+ if o.IsFalsy() {
+ return objects.FalseValue, nil
+ }
+ return objects.TrueValue, nil
+ case *objects.Undefined:
+ return objects.UndefinedValue, nil
+ case *objects.Array:
+ for i, v := range o.Value {
+ fv, err := fixDecoded(v, modules)
+ if err != nil {
+ return nil, err
+ }
+ o.Value[i] = fv
+ }
+ case *objects.ImmutableArray:
+ for i, v := range o.Value {
+ fv, err := fixDecoded(v, modules)
+ if err != nil {
+ return nil, err
+ }
+ o.Value[i] = fv
+ }
+ case *objects.Map:
+ for k, v := range o.Value {
+ fv, err := fixDecoded(v, modules)
+ if err != nil {
+ return nil, err
+ }
+ o.Value[k] = fv
+ }
+ case *objects.ImmutableMap:
+ modName := moduleName(o)
+ if mod := modules.GetBuiltinModule(modName); mod != nil {
+ return mod.AsImmutableMap(modName), nil
+ }
+
+ for k, v := range o.Value {
+ // encoding of user function not supported
+ if _, isUserFunction := v.(*objects.UserFunction); isUserFunction {
+ return nil, fmt.Errorf("user function not decodable")
+ }
+
+ fv, err := fixDecoded(v, modules)
+ if err != nil {
+ return nil, err
+ }
+ o.Value[k] = fv
+ }
+ }
+
+ return o, nil
+}
diff --git a/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go b/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go
new file mode 100644
index 00000000..9526de2e
--- /dev/null
+++ b/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go
@@ -0,0 +1,129 @@
+package compiler
+
+import (
+ "fmt"
+
+ "github.com/d5/tengo/objects"
+)
+
+// RemoveDuplicates finds and remove the duplicate values in Constants.
+// Note this function mutates Bytecode.
+func (b *Bytecode) RemoveDuplicates() {
+ var deduped []objects.Object
+
+ indexMap := make(map[int]int) // mapping from old constant index to new index
+ ints := make(map[int64]int)
+ strings := make(map[string]int)
+ floats := make(map[float64]int)
+ chars := make(map[rune]int)
+ immutableMaps := make(map[string]int) // for modules
+
+ for curIdx, c := range b.Constants {
+ switch c := c.(type) {
+ case *objects.CompiledFunction:
+ // add to deduped list
+ indexMap[curIdx] = len(deduped)
+ deduped = append(deduped, c)
+ case *objects.ImmutableMap:
+ modName := moduleName(c)
+ newIdx, ok := immutableMaps[modName]
+ if modName != "" && ok {
+ indexMap[curIdx] = newIdx
+ } else {
+ newIdx = len(deduped)
+ immutableMaps[modName] = newIdx
+ indexMap[curIdx] = newIdx
+ deduped = append(deduped, c)
+ }
+ case *objects.Int:
+ if newIdx, ok := ints[c.Value]; ok {
+ indexMap[curIdx] = newIdx
+ } else {
+ newIdx = len(deduped)
+ ints[c.Value] = newIdx
+ indexMap[curIdx] = newIdx
+ deduped = append(deduped, c)
+ }
+ case *objects.String:
+ if newIdx, ok := strings[c.Value]; ok {
+ indexMap[curIdx] = newIdx
+ } else {
+ newIdx = len(deduped)
+ strings[c.Value] = newIdx
+ indexMap[curIdx] = newIdx
+ deduped = append(deduped, c)
+ }
+ case *objects.Float:
+ if newIdx, ok := floats[c.Value]; ok {
+ indexMap[curIdx] = newIdx
+ } else {
+ newIdx = len(deduped)
+ floats[c.Value] = newIdx
+ indexMap[curIdx] = newIdx
+ deduped = append(deduped, c)
+ }
+ case *objects.Char:
+ if newIdx, ok := chars[c.Value]; ok {
+ indexMap[curIdx] = newIdx
+ } else {
+ newIdx = len(deduped)
+ chars[c.Value] = newIdx
+ indexMap[curIdx] = newIdx
+ deduped = append(deduped, c)
+ }
+ default:
+ panic(fmt.Errorf("unsupported top-level constant type: %s", c.TypeName()))
+ }
+ }
+
+ // replace with de-duplicated constants
+ b.Constants = deduped
+
+ // update CONST instructions with new indexes
+ // main function
+ updateConstIndexes(b.MainFunction.Instructions, indexMap)
+ // other compiled functions in constants
+ for _, c := range b.Constants {
+ switch c := c.(type) {
+ case *objects.CompiledFunction:
+ updateConstIndexes(c.Instructions, indexMap)
+ }
+ }
+}
+
+func updateConstIndexes(insts []byte, indexMap map[int]int) {
+ i := 0
+ for i < len(insts) {
+ op := insts[i]
+ numOperands := OpcodeOperands[op]
+ _, read := ReadOperands(numOperands, insts[i+1:])
+
+ switch op {
+ case OpConstant:
+ curIdx := int(insts[i+2]) | int(insts[i+1])<<8
+ newIdx, ok := indexMap[curIdx]
+ if !ok {
+ panic(fmt.Errorf("constant index not found: %d", curIdx))
+ }
+ copy(insts[i:], MakeInstruction(op, newIdx))
+ case OpClosure:
+ curIdx := int(insts[i+2]) | int(insts[i+1])<<8
+ numFree := int(insts[i+3])
+ newIdx, ok := indexMap[curIdx]
+ if !ok {
+ panic(fmt.Errorf("constant index not found: %d", curIdx))
+ }
+ copy(insts[i:], MakeInstruction(op, newIdx, numFree))
+ }
+
+ i += 1 + read
+ }
+}
+
+func moduleName(mod *objects.ImmutableMap) string {
+ if modName, ok := mod.Value["__module_name__"].(*objects.String); ok {
+ return modName.Value
+ }
+
+ return ""
+}
diff --git a/vendor/github.com/d5/tengo/compiler/compilation_scope.go b/vendor/github.com/d5/tengo/compiler/compilation_scope.go
index dd198ae9..41e7876b 100644
--- a/vendor/github.com/d5/tengo/compiler/compilation_scope.go
+++ b/vendor/github.com/d5/tengo/compiler/compilation_scope.go
@@ -5,8 +5,7 @@ import "github.com/d5/tengo/compiler/source"
// CompilationScope represents a compiled instructions
// and the last two instructions that were emitted.
type CompilationScope struct {
- instructions []byte
- lastInstructions [2]EmittedInstruction
- symbolInit map[string]bool
- sourceMap map[int]source.Pos
+ instructions []byte
+ symbolInit map[string]bool
+ sourceMap map[int]source.Pos
}
diff --git a/vendor/github.com/d5/tengo/compiler/compiler.go b/vendor/github.com/d5/tengo/compiler/compiler.go
index d8bc05fd..4a3ec3ad 100644
--- a/vendor/github.com/d5/tengo/compiler/compiler.go
+++ b/vendor/github.com/d5/tengo/compiler/compiler.go
@@ -3,7 +3,10 @@ package compiler
import (
"fmt"
"io"
+ "io/ioutil"
+ "path/filepath"
"reflect"
+ "strings"
"github.com/d5/tengo"
"github.com/d5/tengo/compiler/ast"
@@ -16,14 +19,14 @@ import (
type Compiler struct {
file *source.File
parent *Compiler
- moduleName string
+ modulePath string
constants []objects.Object
symbolTable *SymbolTable
scopes []CompilationScope
scopeIndex int
- moduleLoader ModuleLoader
- builtinModules map[string]bool
+ modules *objects.ModuleMap
compiledModules map[string]*objects.CompiledFunction
+ allowFileImport bool
loops []*Loop
loopIndex int
trace io.Writer
@@ -31,12 +34,7 @@ type Compiler struct {
}
// NewCompiler creates a Compiler.
-// User can optionally provide the symbol table if one wants to add or remove
-// some global- or builtin- scope symbols. If not (nil), Compile will create
-// a new symbol table and use the default builtin functions. Likewise, standard
-// modules can be explicitly provided if user wants to add or remove some modules.
-// By default, Compile will use all the standard modules otherwise.
-func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, builtinModules map[string]bool, trace io.Writer) *Compiler {
+func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, modules *objects.ModuleMap, trace io.Writer) *Compiler {
mainScope := CompilationScope{
symbolInit: make(map[string]bool),
sourceMap: make(map[int]source.Pos),
@@ -45,15 +43,16 @@ func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []object
// symbol table
if symbolTable == nil {
symbolTable = NewSymbolTable()
+ }
- for idx, fn := range objects.Builtins {
- symbolTable.DefineBuiltin(idx, fn.Name)
- }
+ // add builtin functions to the symbol table
+ for idx, fn := range objects.Builtins {
+ symbolTable.DefineBuiltin(idx, fn.Name)
}
// builtin modules
- if builtinModules == nil {
- builtinModules = make(map[string]bool)
+ if modules == nil {
+ modules = objects.NewModuleMap()
}
return &Compiler{
@@ -64,7 +63,7 @@ func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []object
scopeIndex: 0,
loopIndex: -1,
trace: trace,
- builtinModules: builtinModules,
+ modules: modules,
compiledModules: make(map[string]*objects.CompiledFunction),
}
}
@@ -120,7 +119,7 @@ func (c *Compiler) Compile(node ast.Node) error {
return err
}
- c.emit(node, OpGreaterThan)
+ c.emit(node, OpBinaryOp, int(token.Greater))
return nil
} else if node.Token == token.LessEq {
@@ -131,7 +130,7 @@ func (c *Compiler) Compile(node ast.Node) error {
return err
}
- c.emit(node, OpGreaterThanEqual)
+ c.emit(node, OpBinaryOp, int(token.GreaterEq))
return nil
}
@@ -145,35 +144,35 @@ func (c *Compiler) Compile(node ast.Node) error {
switch node.Token {
case token.Add:
- c.emit(node, OpAdd)
+ c.emit(node, OpBinaryOp, int(token.Add))
case token.Sub:
- c.emit(node, OpSub)
+ c.emit(node, OpBinaryOp, int(token.Sub))
case token.Mul:
- c.emit(node, OpMul)
+ c.emit(node, OpBinaryOp, int(token.Mul))
case token.Quo:
- c.emit(node, OpDiv)
+ c.emit(node, OpBinaryOp, int(token.Quo))
case token.Rem:
- c.emit(node, OpRem)
+ c.emit(node, OpBinaryOp, int(token.Rem))
case token.Greater:
- c.emit(node, OpGreaterThan)
+ c.emit(node, OpBinaryOp, int(token.Greater))
case token.GreaterEq:
- c.emit(node, OpGreaterThanEqual)
+ c.emit(node, OpBinaryOp, int(token.GreaterEq))
case token.Equal:
c.emit(node, OpEqual)
case token.NotEqual:
c.emit(node, OpNotEqual)
case token.And:
- c.emit(node, OpBAnd)
+ c.emit(node, OpBinaryOp, int(token.And))
case token.Or:
- c.emit(node, OpBOr)
+ c.emit(node, OpBinaryOp, int(token.Or))
case token.Xor:
- c.emit(node, OpBXor)
+ c.emit(node, OpBinaryOp, int(token.Xor))
case token.AndNot:
- c.emit(node, OpBAndNot)
+ c.emit(node, OpBinaryOp, int(token.AndNot))
case token.Shl:
- c.emit(node, OpBShiftLeft)
+ c.emit(node, OpBinaryOp, int(token.Shl))
case token.Shr:
- c.emit(node, OpBShiftRight)
+ c.emit(node, OpBinaryOp, int(token.Shr))
default:
return c.errorf(node, "invalid binary operator: %s", node.Token.String())
}
@@ -293,6 +292,15 @@ func (c *Compiler) Compile(node ast.Node) error {
}
case *ast.BlockStmt:
+ if len(node.Stmts) == 0 {
+ return nil
+ }
+
+ c.symbolTable = c.symbolTable.Fork(true)
+ defer func() {
+ c.symbolTable = c.symbolTable.Parent(false)
+ }()
+
for _, stmt := range node.Stmts {
if err := c.Compile(stmt); err != nil {
return err
@@ -405,10 +413,8 @@ func (c *Compiler) Compile(node ast.Node) error {
return err
}
- // add OpReturn if function returns nothing
- if !c.lastInstructionIs(OpReturnValue) && !c.lastInstructionIs(OpReturn) {
- c.emit(node, OpReturn)
- }
+ // code optimization
+ c.optimizeFunc(node)
freeSymbols := c.symbolTable.FreeSymbols()
numLocals := c.symbolTable.MaxSymbols()
@@ -461,9 +467,9 @@ func (c *Compiler) Compile(node ast.Node) error {
s.LocalAssigned = true
}
- c.emit(node, OpGetLocal, s.Index)
+ c.emit(node, OpGetLocalPtr, s.Index)
case ScopeFree:
- c.emit(node, OpGetFree, s.Index)
+ c.emit(node, OpGetFreePtr, s.Index)
}
}
@@ -487,13 +493,13 @@ func (c *Compiler) Compile(node ast.Node) error {
}
if node.Result == nil {
- c.emit(node, OpReturn)
+ c.emit(node, OpReturn, 0)
} else {
if err := c.Compile(node.Result); err != nil {
return err
}
- c.emit(node, OpReturnValue)
+ c.emit(node, OpReturn, 1)
}
case *ast.CallExpr:
@@ -510,21 +516,57 @@ func (c *Compiler) Compile(node ast.Node) error {
c.emit(node, OpCall, len(node.Args))
case *ast.ImportExpr:
- if c.builtinModules[node.ModuleName] {
- if len(node.ModuleName) > tengo.MaxStringLen {
- return c.error(node, objects.ErrStringLimit)
+ if node.ModuleName == "" {
+ return c.errorf(node, "empty module name")
+ }
+
+ if mod := c.modules.Get(node.ModuleName); mod != nil {
+ v, err := mod.Import(node.ModuleName)
+ if err != nil {
+ return err
}
- c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.ModuleName}))
- c.emit(node, OpGetBuiltinModule)
- } else {
- userMod, err := c.compileModule(node)
+ switch v := v.(type) {
+ case []byte: // module written in Tengo
+ compiled, err := c.compileModule(node, node.ModuleName, node.ModuleName, v)
+ if err != nil {
+ return err
+ }
+ c.emit(node, OpConstant, c.addConstant(compiled))
+ c.emit(node, OpCall, 0)
+ case objects.Object: // builtin module
+ c.emit(node, OpConstant, c.addConstant(v))
+ default:
+ panic(fmt.Errorf("invalid import value type: %T", v))
+ }
+ } else if c.allowFileImport {
+ moduleName := node.ModuleName
+ if !strings.HasSuffix(moduleName, ".tengo") {
+ moduleName += ".tengo"
+ }
+
+ modulePath, err := filepath.Abs(moduleName)
if err != nil {
+ return c.errorf(node, "module file path error: %s", err.Error())
+ }
+
+ if err := c.checkCyclicImports(node, modulePath); err != nil {
return err
}
- c.emit(node, OpConstant, c.addConstant(userMod))
+ moduleSrc, err := ioutil.ReadFile(moduleName)
+ if err != nil {
+ return c.errorf(node, "module file read error: %s", err.Error())
+ }
+
+ compiled, err := c.compileModule(node, moduleName, modulePath, moduleSrc)
+ if err != nil {
+ return err
+ }
+ c.emit(node, OpConstant, c.addConstant(compiled))
c.emit(node, OpCall, 0)
+ } else {
+ return c.errorf(node, "module '%s' not found", node.ModuleName)
}
case *ast.ExportStmt:
@@ -543,7 +585,7 @@ func (c *Compiler) Compile(node ast.Node) error {
}
c.emit(node, OpImmutable)
- c.emit(node, OpReturnValue)
+ c.emit(node, OpReturn, 1)
case *ast.ErrorExpr:
if err := c.Compile(node.Expr); err != nil {
@@ -602,18 +644,16 @@ func (c *Compiler) Bytecode() *Bytecode {
}
}
-// SetModuleLoader sets or replaces the current module loader.
-// Note that the module loader is used for user modules,
-// not for the standard modules.
-func (c *Compiler) SetModuleLoader(moduleLoader ModuleLoader) {
- c.moduleLoader = moduleLoader
+// EnableFileImport enables or disables module loading from local files.
+// Local file modules are disabled by default.
+func (c *Compiler) EnableFileImport(enable bool) {
+ c.allowFileImport = enable
}
-func (c *Compiler) fork(file *source.File, moduleName string, symbolTable *SymbolTable) *Compiler {
- child := NewCompiler(file, symbolTable, nil, c.builtinModules, c.trace)
- child.moduleName = moduleName // name of the module to compile
- child.parent = c // parent to set to current compiler
- child.moduleLoader = c.moduleLoader // share module loader
+func (c *Compiler) fork(file *source.File, modulePath string, symbolTable *SymbolTable) *Compiler {
+ child := NewCompiler(file, symbolTable, nil, c.modules, c.trace)
+ child.modulePath = modulePath // module file path
+ child.parent = c // parent to set to current compiler
return child
}
@@ -657,33 +697,6 @@ func (c *Compiler) addInstruction(b []byte) int {
return posNewIns
}
-func (c *Compiler) setLastInstruction(op Opcode, pos int) {
- c.scopes[c.scopeIndex].lastInstructions[1] = c.scopes[c.scopeIndex].lastInstructions[0]
-
- c.scopes[c.scopeIndex].lastInstructions[0].Opcode = op
- c.scopes[c.scopeIndex].lastInstructions[0].Position = pos
-}
-
-func (c *Compiler) lastInstructionIs(op Opcode) bool {
- if len(c.currentInstructions()) == 0 {
- return false
- }
-
- return c.scopes[c.scopeIndex].lastInstructions[0].Opcode == op
-}
-
-func (c *Compiler) removeLastInstruction() {
- lastPos := c.scopes[c.scopeIndex].lastInstructions[0].Position
-
- if c.trace != nil {
- c.printTrace(fmt.Sprintf("DELET %s",
- FormatInstructions(c.scopes[c.scopeIndex].instructions[lastPos:], lastPos)[0]))
- }
-
- c.scopes[c.scopeIndex].instructions = c.currentInstructions()[:lastPos]
- c.scopes[c.scopeIndex].lastInstructions[0] = c.scopes[c.scopeIndex].lastInstructions[1]
-}
-
func (c *Compiler) replaceInstruction(pos int, inst []byte) {
copy(c.currentInstructions()[pos:], inst)
@@ -700,6 +713,92 @@ func (c *Compiler) changeOperand(opPos int, operand ...int) {
c.replaceInstruction(opPos, inst)
}
+// optimizeFunc performs some code-level optimization for the current function instructions
+// it removes unreachable (dead code) instructions and adds "returns" instruction if needed.
+func (c *Compiler) optimizeFunc(node ast.Node) {
+ // any instructions between RETURN and the function end
+ // or instructions between RETURN and jump target position
+ // are considered as unreachable.
+
+ // pass 1. identify all jump destinations
+ dsts := make(map[int]bool)
+ iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool {
+ switch opcode {
+ case OpJump, OpJumpFalsy, OpAndJump, OpOrJump:
+ dsts[operands[0]] = true
+ }
+
+ return true
+ })
+
+ var newInsts []byte
+
+ // pass 2. eliminate dead code
+ posMap := make(map[int]int) // old position to new position
+ var dstIdx int
+ var deadCode bool
+ iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool {
+ switch {
+ case opcode == OpReturn:
+ if deadCode {
+ return true
+ }
+ deadCode = true
+ case dsts[pos]:
+ dstIdx++
+ deadCode = false
+ case deadCode:
+ return true
+ }
+
+ posMap[pos] = len(newInsts)
+ newInsts = append(newInsts, MakeInstruction(opcode, operands...)...)
+ return true
+ })
+
+ // pass 3. update jump positions
+ var lastOp Opcode
+ var appendReturn bool
+ endPos := len(c.scopes[c.scopeIndex].instructions)
+ iterateInstructions(newInsts, func(pos int, opcode Opcode, operands []int) bool {
+ switch opcode {
+ case OpJump, OpJumpFalsy, OpAndJump, OpOrJump:
+ newDst, ok := posMap[operands[0]]
+ if ok {
+ copy(newInsts[pos:], MakeInstruction(opcode, newDst))
+ } else if endPos == operands[0] {
+ // there's a jump instruction that jumps to the end of function
+ // compiler should append "return".
+ appendReturn = true
+ } else {
+ panic(fmt.Errorf("invalid jump position: %d", newDst))
+ }
+ }
+ lastOp = opcode
+ return true
+ })
+ if lastOp != OpReturn {
+ appendReturn = true
+ }
+
+ // pass 4. update source map
+ newSourceMap := make(map[int]source.Pos)
+ for pos, srcPos := range c.scopes[c.scopeIndex].sourceMap {
+ newPos, ok := posMap[pos]
+ if ok {
+ newSourceMap[newPos] = srcPos
+ }
+ }
+
+ c.scopes[c.scopeIndex].instructions = newInsts
+ c.scopes[c.scopeIndex].sourceMap = newSourceMap
+
+ // append "return"
+ if appendReturn {
+ c.emit(node, OpReturn, 0)
+ }
+}
+
func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int {
filePos := source.NoPos
if node != nil {
@@ -709,7 +808,6 @@ func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int {
inst := MakeInstruction(opcode, operands...)
pos := c.addInstruction(inst)
c.scopes[c.scopeIndex].sourceMap[pos] = filePos
- c.setLastInstruction(opcode, pos)
if c.trace != nil {
c.printTrace(fmt.Sprintf("EMIT %s",
diff --git a/vendor/github.com/d5/tengo/compiler/compiler_assign.go b/vendor/github.com/d5/tengo/compiler/compiler_assign.go
index 0e086c83..59296f6f 100644
--- a/vendor/github.com/d5/tengo/compiler/compiler_assign.go
+++ b/vendor/github.com/d5/tengo/compiler/compiler_assign.go
@@ -51,27 +51,27 @@ func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.To
switch op {
case token.AddAssign:
- c.emit(node, OpAdd)
+ c.emit(node, OpBinaryOp, int(token.Add))
case token.SubAssign:
- c.emit(node, OpSub)
+ c.emit(node, OpBinaryOp, int(token.Sub))
case token.MulAssign:
- c.emit(node, OpMul)
+ c.emit(node, OpBinaryOp, int(token.Mul))
case token.QuoAssign:
- c.emit(node, OpDiv)
+ c.emit(node, OpBinaryOp, int(token.Quo))
case token.RemAssign:
- c.emit(node, OpRem)
+ c.emit(node, OpBinaryOp, int(token.Rem))
case token.AndAssign:
- c.emit(node, OpBAnd)
+ c.emit(node, OpBinaryOp, int(token.And))
case token.OrAssign:
- c.emit(node, OpBOr)
+ c.emit(node, OpBinaryOp, int(token.Or))
case token.AndNotAssign:
- c.emit(node, OpBAndNot)
+ c.emit(node, OpBinaryOp, int(token.AndNot))
case token.XorAssign:
- c.emit(node, OpBXor)
+ c.emit(node, OpBinaryOp, int(token.Xor))
case token.ShlAssign:
- c.emit(node, OpBShiftLeft)
+ c.emit(node, OpBinaryOp, int(token.Shl))
case token.ShrAssign:
- c.emit(node, OpBShiftRight)
+ c.emit(node, OpBinaryOp, int(token.Shr))
}
// compile selector expressions (right to left)
diff --git a/vendor/github.com/d5/tengo/compiler/compiler_module.go b/vendor/github.com/d5/tengo/compiler/compiler_module.go
index d069bfab..8a3671ce 100644
--- a/vendor/github.com/d5/tengo/compiler/compiler_module.go
+++ b/vendor/github.com/d5/tengo/compiler/compiler_module.go
@@ -1,72 +1,31 @@
package compiler
import (
- "io/ioutil"
- "strings"
-
"github.com/d5/tengo/compiler/ast"
"github.com/d5/tengo/compiler/parser"
"github.com/d5/tengo/objects"
)
-func (c *Compiler) compileModule(expr *ast.ImportExpr) (*objects.CompiledFunction, error) {
- compiledModule, exists := c.loadCompiledModule(expr.ModuleName)
- if exists {
- return compiledModule, nil
+func (c *Compiler) checkCyclicImports(node ast.Node, modulePath string) error {
+ if c.modulePath == modulePath {
+ return c.errorf(node, "cyclic module import: %s", modulePath)
+ } else if c.parent != nil {
+ return c.parent.checkCyclicImports(node, modulePath)
}
- moduleName := expr.ModuleName
-
- // read module source from loader
- var moduleSrc []byte
- if c.moduleLoader == nil {
- // default loader: read from local file
- if !strings.HasSuffix(moduleName, ".tengo") {
- moduleName += ".tengo"
- }
-
- if err := c.checkCyclicImports(expr, moduleName); err != nil {
- return nil, err
- }
-
- var err error
- moduleSrc, err = ioutil.ReadFile(moduleName)
- if err != nil {
- return nil, c.errorf(expr, "module file read error: %s", err.Error())
- }
- } else {
- if err := c.checkCyclicImports(expr, moduleName); err != nil {
- return nil, err
- }
-
- var err error
- moduleSrc, err = c.moduleLoader(moduleName)
- if err != nil {
- return nil, err
- }
- }
+ return nil
+}
- compiledModule, err := c.doCompileModule(moduleName, moduleSrc)
- if err != nil {
+func (c *Compiler) compileModule(node ast.Node, moduleName, modulePath string, src []byte) (*objects.CompiledFunction, error) {
+ if err := c.checkCyclicImports(node, modulePath); err != nil {
return nil, err
}
- c.storeCompiledModule(moduleName, compiledModule)
-
- return compiledModule, nil
-}
-
-func (c *Compiler) checkCyclicImports(node ast.Node, moduleName string) error {
- if c.moduleName == moduleName {
- return c.errorf(node, "cyclic module import: %s", moduleName)
- } else if c.parent != nil {
- return c.parent.checkCyclicImports(node, moduleName)
+ compiledModule, exists := c.loadCompiledModule(modulePath)
+ if exists {
+ return compiledModule, nil
}
- return nil
-}
-
-func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.CompiledFunction, error) {
modFile := c.file.Set().AddFile(moduleName, -1, len(src))
p := parser.NewParser(modFile, src, nil)
file, err := p.ParseFile()
@@ -85,36 +44,36 @@ func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.Comp
symbolTable = symbolTable.Fork(false)
// compile module
- moduleCompiler := c.fork(modFile, moduleName, symbolTable)
+ moduleCompiler := c.fork(modFile, modulePath, symbolTable)
if err := moduleCompiler.Compile(file); err != nil {
return nil, err
}
- // add OpReturn (== export undefined) if export is missing
- if !moduleCompiler.lastInstructionIs(OpReturnValue) {
- moduleCompiler.emit(nil, OpReturn)
- }
+ // code optimization
+ moduleCompiler.optimizeFunc(node)
compiledFunc := moduleCompiler.Bytecode().MainFunction
compiledFunc.NumLocals = symbolTable.MaxSymbols()
+ c.storeCompiledModule(modulePath, compiledFunc)
+
return compiledFunc, nil
}
-func (c *Compiler) loadCompiledModule(moduleName string) (mod *objects.CompiledFunction, ok bool) {
+func (c *Compiler) loadCompiledModule(modulePath string) (mod *objects.CompiledFunction, ok bool) {
if c.parent != nil {
- return c.parent.loadCompiledModule(moduleName)
+ return c.parent.loadCompiledModule(modulePath)
}
- mod, ok = c.compiledModules[moduleName]
+ mod, ok = c.compiledModules[modulePath]
return
}
-func (c *Compiler) storeCompiledModule(moduleName string, module *objects.CompiledFunction) {
+func (c *Compiler) storeCompiledModule(modulePath string, module *objects.CompiledFunction) {
if c.parent != nil {
- c.parent.storeCompiledModule(moduleName, module)
+ c.parent.storeCompiledModule(modulePath, module)
}
- c.compiledModules[moduleName] = module
+ c.compiledModules[modulePath] = module
}
diff --git a/vendor/github.com/d5/tengo/compiler/instructions.go b/vendor/github.com/d5/tengo/compiler/instructions.go
index b04b2826..80c88d13 100644
--- a/vendor/github.com/d5/tengo/compiler/instructions.go
+++ b/vendor/github.com/d5/tengo/compiler/instructions.go
@@ -57,3 +57,16 @@ func FormatInstructions(b []byte, posOffset int) []string {
return out
}
+
+func iterateInstructions(b []byte, fn func(pos int, opcode Opcode, operands []int) bool) {
+ for i := 0; i < len(b); i++ {
+ numOperands := OpcodeOperands[Opcode(b[i])]
+ operands, read := ReadOperands(numOperands, b[i+1:])
+
+ if !fn(i, b[i], operands) {
+ break
+ }
+
+ i += read
+ }
+}
diff --git a/vendor/github.com/d5/tengo/compiler/opcodes.go b/vendor/github.com/d5/tengo/compiler/opcodes.go
index d4cf1ba2..d832ee17 100644
--- a/vendor/github.com/d5/tengo/compiler/opcodes.go
+++ b/vendor/github.com/d5/tengo/compiler/opcodes.go
@@ -5,173 +5,137 @@ type Opcode = byte
// List of opcodes
const (
- OpConstant Opcode = iota // Load constant
- OpAdd // Add
- OpSub // Sub
- OpMul // Multiply
- OpDiv // Divide
- OpRem // Remainder
- OpBAnd // bitwise AND
- OpBOr // bitwise OR
- OpBXor // bitwise XOR
- OpBShiftLeft // bitwise shift left
- OpBShiftRight // bitwise shift right
- OpBAndNot // bitwise AND NOT
- OpBComplement // bitwise complement
- OpPop // Pop
- OpTrue // Push true
- OpFalse // Push false
- OpEqual // Equal ==
- OpNotEqual // Not equal !=
- OpGreaterThan // Greater than >=
- OpGreaterThanEqual // Greater than or equal to >=
- OpMinus // Minus -
- OpLNot // Logical not !
- OpJumpFalsy // Jump if falsy
- OpAndJump // Logical AND jump
- OpOrJump // Logical OR jump
- OpJump // Jump
- OpNull // Push null
- OpArray // Array object
- OpMap // Map object
- OpError // Error object
- OpImmutable // Immutable object
- OpIndex // Index operation
- OpSliceIndex // Slice operation
- OpCall // Call function
- OpReturn // Return
- OpReturnValue // Return value
- OpGetGlobal // Get global variable
- OpSetGlobal // Set global variable
- OpSetSelGlobal // Set global variable using selectors
- OpGetLocal // Get local variable
- OpSetLocal // Set local variable
- OpDefineLocal // Define local variable
- OpSetSelLocal // Set local variable using selectors
- OpGetFree // Get free variables
- OpSetFree // Set free variables
- OpSetSelFree // Set free variables using selectors
- OpGetBuiltin // Get builtin function
- OpGetBuiltinModule // Get builtin module
- OpClosure // Push closure
- OpIteratorInit // Iterator init
- OpIteratorNext // Iterator next
- OpIteratorKey // Iterator key
- OpIteratorValue // Iterator value
+ OpConstant Opcode = iota // Load constant
+ OpBComplement // bitwise complement
+ OpPop // Pop
+ OpTrue // Push true
+ OpFalse // Push false
+ OpEqual // Equal ==
+ OpNotEqual // Not equal !=
+ OpMinus // Minus -
+ OpLNot // Logical not !
+ OpJumpFalsy // Jump if falsy
+ OpAndJump // Logical AND jump
+ OpOrJump // Logical OR jump
+ OpJump // Jump
+ OpNull // Push null
+ OpArray // Array object
+ OpMap // Map object
+ OpError // Error object
+ OpImmutable // Immutable object
+ OpIndex // Index operation
+ OpSliceIndex // Slice operation
+ OpCall // Call function
+ OpReturn // Return
+ OpGetGlobal // Get global variable
+ OpSetGlobal // Set global variable
+ OpSetSelGlobal // Set global variable using selectors
+ OpGetLocal // Get local variable
+ OpSetLocal // Set local variable
+ OpDefineLocal // Define local variable
+ OpSetSelLocal // Set local variable using selectors
+ OpGetFreePtr // Get free variable pointer object
+ OpGetFree // Get free variables
+ OpSetFree // Set free variables
+ OpGetLocalPtr // Get local variable as a pointer
+ OpSetSelFree // Set free variables using selectors
+ OpGetBuiltin // Get builtin function
+ OpClosure // Push closure
+ OpIteratorInit // Iterator init
+ OpIteratorNext // Iterator next
+ OpIteratorKey // Iterator key
+ OpIteratorValue // Iterator value
+ OpBinaryOp // Binary Operation
)
// OpcodeNames is opcode names.
var OpcodeNames = [...]string{
- OpConstant: "CONST",
- OpPop: "POP",
- OpTrue: "TRUE",
- OpFalse: "FALSE",
- OpAdd: "ADD",
- OpSub: "SUB",
- OpMul: "MUL",
- OpDiv: "DIV",
- OpRem: "REM",
- OpBAnd: "AND",
- OpBOr: "OR",
- OpBXor: "XOR",
- OpBAndNot: "ANDN",
- OpBShiftLeft: "SHL",
- OpBShiftRight: "SHR",
- OpBComplement: "NEG",
- OpEqual: "EQL",
- OpNotEqual: "NEQ",
- OpGreaterThan: "GTR",
- OpGreaterThanEqual: "GEQ",
- OpMinus: "NEG",
- OpLNot: "NOT",
- OpJumpFalsy: "JMPF",
- OpAndJump: "ANDJMP",
- OpOrJump: "ORJMP",
- OpJump: "JMP",
- OpNull: "NULL",
- OpGetGlobal: "GETG",
- OpSetGlobal: "SETG",
- OpSetSelGlobal: "SETSG",
- OpArray: "ARR",
- OpMap: "MAP",
- OpError: "ERROR",
- OpImmutable: "IMMUT",
- OpIndex: "INDEX",
- OpSliceIndex: "SLICE",
- OpCall: "CALL",
- OpReturn: "RET",
- OpReturnValue: "RETVAL",
- OpGetLocal: "GETL",
- OpSetLocal: "SETL",
- OpDefineLocal: "DEFL",
- OpSetSelLocal: "SETSL",
- OpGetBuiltin: "BUILTIN",
- OpGetBuiltinModule: "BLTMOD",
- OpClosure: "CLOSURE",
- OpGetFree: "GETF",
- OpSetFree: "SETF",
- OpSetSelFree: "SETSF",
- OpIteratorInit: "ITER",
- OpIteratorNext: "ITNXT",
- OpIteratorKey: "ITKEY",
- OpIteratorValue: "ITVAL",
+ OpConstant: "CONST",
+ OpPop: "POP",
+ OpTrue: "TRUE",
+ OpFalse: "FALSE",
+ OpBComplement: "NEG",
+ OpEqual: "EQL",
+ OpNotEqual: "NEQ",
+ OpMinus: "NEG",
+ OpLNot: "NOT",
+ OpJumpFalsy: "JMPF",
+ OpAndJump: "ANDJMP",
+ OpOrJump: "ORJMP",
+ OpJump: "JMP",
+ OpNull: "NULL",
+ OpGetGlobal: "GETG",
+ OpSetGlobal: "SETG",
+ OpSetSelGlobal: "SETSG",
+ OpArray: "ARR",
+ OpMap: "MAP",
+ OpError: "ERROR",
+ OpImmutable: "IMMUT",
+ OpIndex: "INDEX",
+ OpSliceIndex: "SLICE",
+ OpCall: "CALL",
+ OpReturn: "RET",
+ OpGetLocal: "GETL",
+ OpSetLocal: "SETL",
+ OpDefineLocal: "DEFL",
+ OpSetSelLocal: "SETSL",
+ OpGetBuiltin: "BUILTIN",
+ OpClosure: "CLOSURE",
+ OpGetFreePtr: "GETFP",
+ OpGetFree: "GETF",
+ OpSetFree: "SETF",
+ OpGetLocalPtr: "GETLP",
+ OpSetSelFree: "SETSF",
+ OpIteratorInit: "ITER",
+ OpIteratorNext: "ITNXT",
+ OpIteratorKey: "ITKEY",
+ OpIteratorValue: "ITVAL",
+ OpBinaryOp: "BINARYOP",
}
// OpcodeOperands is the number of operands.
var OpcodeOperands = [...][]int{
- OpConstant: {2},
- OpPop: {},
- OpTrue: {},
- OpFalse: {},
- OpAdd: {},
- OpSub: {},
- OpMul: {},
- OpDiv: {},
- OpRem: {},
- OpBAnd: {},
- OpBOr: {},
- OpBXor: {},
- OpBAndNot: {},
- OpBShiftLeft: {},
- OpBShiftRight: {},
- OpBComplement: {},
- OpEqual: {},
- OpNotEqual: {},
- OpGreaterThan: {},
- OpGreaterThanEqual: {},
- OpMinus: {},
- OpLNot: {},
- OpJumpFalsy: {2},
- OpAndJump: {2},
- OpOrJump: {2},
- OpJump: {2},
- OpNull: {},
- OpGetGlobal: {2},
- OpSetGlobal: {2},
- OpSetSelGlobal: {2, 1},
- OpArray: {2},
- OpMap: {2},
- OpError: {},
- OpImmutable: {},
- OpIndex: {},
- OpSliceIndex: {},
- OpCall: {1},
- OpReturn: {},
- OpReturnValue: {},
- OpGetLocal: {1},
- OpSetLocal: {1},
- OpDefineLocal: {1},
- OpSetSelLocal: {1, 1},
- OpGetBuiltin: {1},
- OpGetBuiltinModule: {},
- OpClosure: {2, 1},
- OpGetFree: {1},
- OpSetFree: {1},
- OpSetSelFree: {1, 1},
- OpIteratorInit: {},
- OpIteratorNext: {},
- OpIteratorKey: {},
- OpIteratorValue: {},
+ OpConstant: {2},
+ OpPop: {},
+ OpTrue: {},
+ OpFalse: {},
+ OpBComplement: {},
+ OpEqual: {},
+ OpNotEqual: {},
+ OpMinus: {},
+ OpLNot: {},
+ OpJumpFalsy: {2},
+ OpAndJump: {2},
+ OpOrJump: {2},
+ OpJump: {2},
+ OpNull: {},
+ OpGetGlobal: {2},
+ OpSetGlobal: {2},
+ OpSetSelGlobal: {2, 1},
+ OpArray: {2},
+ OpMap: {2},
+ OpError: {},
+ OpImmutable: {},
+ OpIndex: {},
+ OpSliceIndex: {},
+ OpCall: {1},
+ OpReturn: {1},
+ OpGetLocal: {1},
+ OpSetLocal: {1},
+ OpDefineLocal: {1},
+ OpSetSelLocal: {1, 1},
+ OpGetBuiltin: {1},
+ OpClosure: {2, 1},
+ OpGetFreePtr: {1},
+ OpGetFree: {1},
+ OpSetFree: {1},
+ OpGetLocalPtr: {1},
+ OpSetSelFree: {1, 1},
+ OpIteratorInit: {},
+ OpIteratorNext: {},
+ OpIteratorKey: {},
+ OpIteratorValue: {},
+ OpBinaryOp: {1},
}
// ReadOperands reads operands from the bytecode.
diff --git a/vendor/github.com/d5/tengo/compiler/symbol_table.go b/vendor/github.com/d5/tengo/compiler/symbol_table.go
index fb029b31..94c868de 100644
--- a/vendor/github.com/d5/tengo/compiler/symbol_table.go
+++ b/vendor/github.com/d5/tengo/compiler/symbol_table.go
@@ -64,9 +64,7 @@ func (t *SymbolTable) Resolve(name string) (symbol *Symbol, depth int, ok bool)
return
}
- if !t.block {
- depth++
- }
+ depth++
// if symbol is defined in parent table and if it's not global/builtin
// then it's free variable.
diff --git a/vendor/github.com/d5/tengo/objects/break.go b/vendor/github.com/d5/tengo/objects/break.go
deleted file mode 100644
index cd473a87..00000000
--- a/vendor/github.com/d5/tengo/objects/break.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package objects
-
-import "github.com/d5/tengo/compiler/token"
-
-// Break represents a break statement.
-type Break struct{}
-
-// TypeName returns the name of the type.
-func (o *Break) TypeName() string {
- return "break"
-}
-
-func (o *Break) String() string {
- return "<break>"
-}
-
-// BinaryOp returns another object that is the result of
-// a given binary operator and a right-hand side object.
-func (o *Break) BinaryOp(op token.Token, rhs Object) (Object, error) {
- return nil, ErrInvalidOperator
-}
-
-// Copy returns a copy of the type.
-func (o *Break) Copy() Object {
- return &Break{}
-}
-
-// IsFalsy returns true if the value of the type is falsy.
-func (o *Break) IsFalsy() bool {
- return false
-}
-
-// Equals returns true if the value of the type
-// is equal to the value of another object.
-func (o *Break) Equals(x Object) bool {
- return false
-}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_json.go b/vendor/github.com/d5/tengo/objects/builtin_json.go
deleted file mode 100644
index b3413651..00000000
--- a/vendor/github.com/d5/tengo/objects/builtin_json.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package objects
-
-import (
- "encoding/json"
-
- "github.com/d5/tengo"
-)
-
-// to_json(v object) => bytes
-func builtinToJSON(args ...Object) (Object, error) {
- if len(args) != 1 {
- return nil, ErrWrongNumArguments
- }
-
- res, err := json.Marshal(objectToInterface(args[0]))
- if err != nil {
- return &Error{Value: &String{Value: err.Error()}}, nil
- }
-
- if len(res) > tengo.MaxBytesLen {
- return nil, ErrBytesLimit
- }
-
- return &Bytes{Value: res}, nil
-}
-
-// from_json(data string/bytes) => object
-func builtinFromJSON(args ...Object) (Object, error) {
- if len(args) != 1 {
- return nil, ErrWrongNumArguments
- }
-
- var target interface{}
-
- switch o := args[0].(type) {
- case *Bytes:
- err := json.Unmarshal(o.Value, &target)
- if err != nil {
- return &Error{Value: &String{Value: err.Error()}}, nil
- }
- case *String:
- err := json.Unmarshal([]byte(o.Value), &target)
- if err != nil {
- return &Error{Value: &String{Value: err.Error()}}, nil
- }
- default:
- return nil, ErrInvalidArgumentType{
- Name: "first",
- Expected: "bytes/string",
- Found: args[0].TypeName(),
- }
- }
-
- res, err := FromInterface(target)
- if err != nil {
- return nil, err
- }
-
- return res, nil
-}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_module.go b/vendor/github.com/d5/tengo/objects/builtin_module.go
new file mode 100644
index 00000000..0ad1d99d
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_module.go
@@ -0,0 +1,23 @@
+package objects
+
+// BuiltinModule is an importable module that's written in Go.
+type BuiltinModule struct {
+ Attrs map[string]Object
+}
+
+// Import returns an immutable map for the module.
+func (m *BuiltinModule) Import(moduleName string) (interface{}, error) {
+ return m.AsImmutableMap(moduleName), nil
+}
+
+// AsImmutableMap converts builtin module into an immutable map.
+func (m *BuiltinModule) AsImmutableMap(moduleName string) *ImmutableMap {
+ attrs := make(map[string]Object, len(m.Attrs))
+ for k, v := range m.Attrs {
+ attrs[k] = v.Copy()
+ }
+
+ attrs["__module_name__"] = &String{Value: moduleName}
+
+ return &ImmutableMap{Value: attrs}
+}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_print.go b/vendor/github.com/d5/tengo/objects/builtin_print.go
deleted file mode 100644
index 58f22610..00000000
--- a/vendor/github.com/d5/tengo/objects/builtin_print.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package objects
-
-import (
- "fmt"
-
- "github.com/d5/tengo"
-)
-
-// print(args...)
-func builtinPrint(args ...Object) (Object, error) {
- for _, arg := range args {
- if str, ok := arg.(*String); ok {
- fmt.Println(str.Value)
- } else {
- fmt.Println(arg.String())
- }
- }
-
- return nil, nil
-}
-
-// printf("format", args...)
-func builtinPrintf(args ...Object) (Object, error) {
- numArgs := len(args)
- if numArgs == 0 {
- return nil, ErrWrongNumArguments
- }
-
- format, ok := args[0].(*String)
- if !ok {
- return nil, ErrInvalidArgumentType{
- Name: "format",
- Expected: "string",
- Found: args[0].TypeName(),
- }
- }
- if numArgs == 1 {
- fmt.Print(format)
- return nil, nil
- }
-
- formatArgs := make([]interface{}, numArgs-1, numArgs-1)
- for idx, arg := range args[1:] {
- formatArgs[idx] = objectToInterface(arg)
- }
-
- fmt.Printf(format.Value, formatArgs...)
-
- return nil, nil
-}
-
-// sprintf("format", args...)
-func builtinSprintf(args ...Object) (Object, error) {
- numArgs := len(args)
- if numArgs == 0 {
- return nil, ErrWrongNumArguments
- }
-
- format, ok := args[0].(*String)
- if !ok {
- return nil, ErrInvalidArgumentType{
- Name: "format",
- Expected: "string",
- Found: args[0].TypeName(),
- }
- }
- if numArgs == 1 {
- return format, nil // okay to return 'format' directly as String is immutable
- }
-
- formatArgs := make([]interface{}, numArgs-1, numArgs-1)
- for idx, arg := range args[1:] {
- formatArgs[idx] = objectToInterface(arg)
- }
-
- s := fmt.Sprintf(format.Value, formatArgs...)
-
- if len(s) > tengo.MaxStringLen {
- return nil, ErrStringLimit
- }
-
- return &String{Value: s}, nil
-}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_type_checks.go b/vendor/github.com/d5/tengo/objects/builtin_type_checks.go
index 960f7828..d1e8471d 100644
--- a/vendor/github.com/d5/tengo/objects/builtin_type_checks.go
+++ b/vendor/github.com/d5/tengo/objects/builtin_type_checks.go
@@ -181,3 +181,15 @@ func builtinIsCallable(args ...Object) (Object, error) {
return FalseValue, nil
}
+
+func builtinIsIterable(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(Iterable); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
diff --git a/vendor/github.com/d5/tengo/objects/builtins.go b/vendor/github.com/d5/tengo/objects/builtins.go
index 42c1a759..bfd004dd 100644
--- a/vendor/github.com/d5/tengo/objects/builtins.go
+++ b/vendor/github.com/d5/tengo/objects/builtins.go
@@ -2,19 +2,7 @@ package objects
// Builtins contains all default builtin functions.
// Use GetBuiltinFunctions instead of accessing Builtins directly.
-var Builtins = []BuiltinFunction{
- {
- Name: "print",
- Value: builtinPrint,
- },
- {
- Name: "printf",
- Value: builtinPrintf,
- },
- {
- Name: "sprintf",
- Value: builtinSprintf,
- },
+var Builtins = []*BuiltinFunction{
{
Name: "len",
Value: builtinLen,
@@ -96,6 +84,10 @@ var Builtins = []BuiltinFunction{
Value: builtinIsImmutableMap,
},
{
+ Name: "is_iterable",
+ Value: builtinIsIterable,
+ },
+ {
Name: "is_time",
Value: builtinIsTime,
},
@@ -116,49 +108,7 @@ var Builtins = []BuiltinFunction{
Value: builtinIsCallable,
},
{
- Name: "to_json",
- Value: builtinToJSON,
- },
- {
- Name: "from_json",
- Value: builtinFromJSON,
- },
- {
Name: "type_name",
Value: builtinTypeName,
},
}
-
-// AllBuiltinFunctionNames returns a list of all default builtin function names.
-func AllBuiltinFunctionNames() []string {
- var names []string
- for _, bf := range Builtins {
- names = append(names, bf.Name)
- }
- return names
-}
-
-// GetBuiltinFunctions returns a slice of builtin function objects.
-// GetBuiltinFunctions removes the duplicate names, and, the returned builtin functions
-// are not guaranteed to be in the same order as names.
-func GetBuiltinFunctions(names ...string) []*BuiltinFunction {
- include := make(map[string]bool)
- for _, name := range names {
- include[name] = true
- }
-
- var builtinFuncs []*BuiltinFunction
- for _, bf := range Builtins {
- if include[bf.Name] {
- bf := bf
- builtinFuncs = append(builtinFuncs, &bf)
- }
- }
-
- return builtinFuncs
-}
-
-// GetAllBuiltinFunctions returns all builtin functions.
-func GetAllBuiltinFunctions() []*BuiltinFunction {
- return GetBuiltinFunctions(AllBuiltinFunctionNames()...)
-}
diff --git a/vendor/github.com/d5/tengo/objects/bytes.go b/vendor/github.com/d5/tengo/objects/bytes.go
index 16b61684..6710c7c1 100644
--- a/vendor/github.com/d5/tengo/objects/bytes.go
+++ b/vendor/github.com/d5/tengo/objects/bytes.go
@@ -79,3 +79,11 @@ func (o *Bytes) IndexGet(index Object) (res Object, err error) {
return
}
+
+// Iterate creates a bytes iterator.
+func (o *Bytes) Iterate() Iterator {
+ return &BytesIterator{
+ v: o.Value,
+ l: len(o.Value),
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/bytes_iterator.go b/vendor/github.com/d5/tengo/objects/bytes_iterator.go
new file mode 100644
index 00000000..18a36e17
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/bytes_iterator.go
@@ -0,0 +1,57 @@
+package objects
+
+import "github.com/d5/tengo/compiler/token"
+
+// BytesIterator represents an iterator for a string.
+type BytesIterator struct {
+ v []byte
+ i int
+ l int
+}
+
+// TypeName returns the name of the type.
+func (i *BytesIterator) TypeName() string {
+ return "bytes-iterator"
+}
+
+func (i *BytesIterator) String() string {
+ return "<bytes-iterator>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (i *BytesIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (i *BytesIterator) IsFalsy() bool {
+ return true
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (i *BytesIterator) Equals(Object) bool {
+ return false
+}
+
+// Copy returns a copy of the type.
+func (i *BytesIterator) Copy() Object {
+ return &BytesIterator{v: i.v, i: i.i, l: i.l}
+}
+
+// Next returns true if there are more elements to iterate.
+func (i *BytesIterator) Next() bool {
+ i.i++
+ return i.i <= i.l
+}
+
+// Key returns the key or index value of the current element.
+func (i *BytesIterator) Key() Object {
+ return &Int{Value: int64(i.i - 1)}
+}
+
+// Value returns the value of the current element.
+func (i *BytesIterator) Value() Object {
+ return &Int{Value: int64(i.v[i.i-1])}
+}
diff --git a/vendor/github.com/d5/tengo/objects/closure.go b/vendor/github.com/d5/tengo/objects/closure.go
index d4915a52..06058b23 100644
--- a/vendor/github.com/d5/tengo/objects/closure.go
+++ b/vendor/github.com/d5/tengo/objects/closure.go
@@ -7,7 +7,7 @@ import (
// Closure represents a function closure.
type Closure struct {
Fn *CompiledFunction
- Free []*Object
+ Free []*ObjectPtr
}
// TypeName returns the name of the type.
@@ -29,7 +29,7 @@ func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) {
func (o *Closure) Copy() Object {
return &Closure{
Fn: o.Fn.Copy().(*CompiledFunction),
- Free: append([]*Object{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers
+ Free: append([]*ObjectPtr{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers
}
}
diff --git a/vendor/github.com/d5/tengo/objects/compiled_function.go b/vendor/github.com/d5/tengo/objects/compiled_function.go
index d20f2375..606e3d90 100644
--- a/vendor/github.com/d5/tengo/objects/compiled_function.go
+++ b/vendor/github.com/d5/tengo/objects/compiled_function.go
@@ -47,3 +47,14 @@ func (o *CompiledFunction) IsFalsy() bool {
func (o *CompiledFunction) Equals(x Object) bool {
return false
}
+
+// SourcePos returns the source position of the instruction at ip.
+func (o *CompiledFunction) SourcePos(ip int) source.Pos {
+ for ip >= 0 {
+ if p, ok := o.SourceMap[ip]; ok {
+ return p
+ }
+ ip--
+ }
+ return source.NoPos
+}
diff --git a/vendor/github.com/d5/tengo/objects/continue.go b/vendor/github.com/d5/tengo/objects/continue.go
deleted file mode 100644
index 8094e686..00000000
--- a/vendor/github.com/d5/tengo/objects/continue.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package objects
-
-import "github.com/d5/tengo/compiler/token"
-
-// Continue represents a continue statement.
-type Continue struct {
-}
-
-// TypeName returns the name of the type.
-func (o *Continue) TypeName() string {
- return "continue"
-}
-
-func (o *Continue) String() string {
- return "<continue>"
-}
-
-// BinaryOp returns another object that is the result of
-// a given binary operator and a right-hand side object.
-func (o *Continue) BinaryOp(op token.Token, rhs Object) (Object, error) {
- return nil, ErrInvalidOperator
-}
-
-// Copy returns a copy of the type.
-func (o *Continue) Copy() Object {
- return &Continue{}
-}
-
-// IsFalsy returns true if the value of the type is falsy.
-func (o *Continue) IsFalsy() bool {
- return false
-}
-
-// Equals returns true if the value of the type
-// is equal to the value of another object.
-func (o *Continue) Equals(x Object) bool {
- return false
-}
diff --git a/vendor/github.com/d5/tengo/objects/conversion.go b/vendor/github.com/d5/tengo/objects/conversion.go
index 714f2617..d7cb3a03 100644
--- a/vendor/github.com/d5/tengo/objects/conversion.go
+++ b/vendor/github.com/d5/tengo/objects/conversion.go
@@ -1,6 +1,7 @@
package objects
import (
+ "errors"
"fmt"
"strconv"
"time"
@@ -158,8 +159,8 @@ func ToTime(o Object) (v time.Time, ok bool) {
return
}
-// objectToInterface attempts to convert an object o to an interface{} value
-func objectToInterface(o Object) (res interface{}) {
+// ToInterface attempts to convert an object o to an interface{} value
+func ToInterface(o Object) (res interface{}) {
switch o := o.(type) {
case *Int:
res = o.Value
@@ -176,13 +177,29 @@ func objectToInterface(o Object) (res interface{}) {
case *Array:
res = make([]interface{}, len(o.Value))
for i, val := range o.Value {
- res.([]interface{})[i] = objectToInterface(val)
+ res.([]interface{})[i] = ToInterface(val)
+ }
+ case *ImmutableArray:
+ res = make([]interface{}, len(o.Value))
+ for i, val := range o.Value {
+ res.([]interface{})[i] = ToInterface(val)
}
case *Map:
res = make(map[string]interface{})
for key, v := range o.Value {
- res.(map[string]interface{})[key] = objectToInterface(v)
+ res.(map[string]interface{})[key] = ToInterface(v)
+ }
+ case *ImmutableMap:
+ res = make(map[string]interface{})
+ for key, v := range o.Value {
+ res.(map[string]interface{})[key] = ToInterface(v)
}
+ case *Time:
+ res = o.Value
+ case *Error:
+ res = errors.New(o.String())
+ case *Undefined:
+ res = nil
case Object:
return o
}
diff --git a/vendor/github.com/d5/tengo/objects/count_objects.go b/vendor/github.com/d5/tengo/objects/count_objects.go
new file mode 100644
index 00000000..8c482eb3
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/count_objects.go
@@ -0,0 +1,31 @@
+package objects
+
+// CountObjects returns the number of objects that a given object o contains.
+// For scalar value types, it will always be 1. For compound value types,
+// this will include its elements and all of their elements recursively.
+func CountObjects(o Object) (c int) {
+ c = 1
+
+ switch o := o.(type) {
+ case *Array:
+ for _, v := range o.Value {
+ c += CountObjects(v)
+ }
+ case *ImmutableArray:
+ for _, v := range o.Value {
+ c += CountObjects(v)
+ }
+ case *Map:
+ for _, v := range o.Value {
+ c += CountObjects(v)
+ }
+ case *ImmutableMap:
+ for _, v := range o.Value {
+ c += CountObjects(v)
+ }
+ case *Error:
+ c += CountObjects(o.Value)
+ }
+
+ return
+}
diff --git a/vendor/github.com/d5/tengo/objects/importable.go b/vendor/github.com/d5/tengo/objects/importable.go
new file mode 100644
index 00000000..9fd86ae8
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/importable.go
@@ -0,0 +1,7 @@
+package objects
+
+// Importable interface represents importable module instance.
+type Importable interface {
+ // Import should return either an Object or module source code ([]byte).
+ Import(moduleName string) (interface{}, error)
+}
diff --git a/vendor/github.com/d5/tengo/objects/module_map.go b/vendor/github.com/d5/tengo/objects/module_map.go
new file mode 100644
index 00000000..874b8a2b
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/module_map.go
@@ -0,0 +1,77 @@
+package objects
+
+// ModuleMap represents a set of named modules.
+// Use NewModuleMap to create a new module map.
+type ModuleMap struct {
+ m map[string]Importable
+}
+
+// NewModuleMap creates a new module map.
+func NewModuleMap() *ModuleMap {
+ return &ModuleMap{
+ m: make(map[string]Importable),
+ }
+}
+
+// Add adds an import module.
+func (m *ModuleMap) Add(name string, module Importable) {
+ m.m[name] = module
+}
+
+// AddBuiltinModule adds a builtin module.
+func (m *ModuleMap) AddBuiltinModule(name string, attrs map[string]Object) {
+ m.m[name] = &BuiltinModule{Attrs: attrs}
+}
+
+// AddSourceModule adds a source module.
+func (m *ModuleMap) AddSourceModule(name string, src []byte) {
+ m.m[name] = &SourceModule{Src: src}
+}
+
+// Remove removes a named module.
+func (m *ModuleMap) Remove(name string) {
+ delete(m.m, name)
+}
+
+// Get returns an import module identified by name.
+// It returns if the name is not found.
+func (m *ModuleMap) Get(name string) Importable {
+ return m.m[name]
+}
+
+// GetBuiltinModule returns a builtin module identified by name.
+// It returns if the name is not found or the module is not a builtin module.
+func (m *ModuleMap) GetBuiltinModule(name string) *BuiltinModule {
+ mod, _ := m.m[name].(*BuiltinModule)
+ return mod
+}
+
+// GetSourceModule returns a source module identified by name.
+// It returns if the name is not found or the module is not a source module.
+func (m *ModuleMap) GetSourceModule(name string) *SourceModule {
+ mod, _ := m.m[name].(*SourceModule)
+ return mod
+}
+
+// Copy creates a copy of the module map.
+func (m *ModuleMap) Copy() *ModuleMap {
+ c := &ModuleMap{
+ m: make(map[string]Importable),
+ }
+ for name, mod := range m.m {
+ c.m[name] = mod
+ }
+ return c
+}
+
+// Len returns the number of named modules.
+func (m *ModuleMap) Len() int {
+ return len(m.m)
+}
+
+// AddMap adds named modules from another module map.
+func (m *ModuleMap) AddMap(o *ModuleMap) {
+ for name, mod := range o.m {
+ m.m[name] = mod
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/object_ptr.go b/vendor/github.com/d5/tengo/objects/object_ptr.go
new file mode 100644
index 00000000..2c87c561
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/object_ptr.go
@@ -0,0 +1,41 @@
+package objects
+
+import (
+ "github.com/d5/tengo/compiler/token"
+)
+
+// ObjectPtr represents a free variable.
+type ObjectPtr struct {
+ Value *Object
+}
+
+func (o *ObjectPtr) String() string {
+ return "free-var"
+}
+
+// TypeName returns the name of the type.
+func (o *ObjectPtr) TypeName() string {
+ return "<free-var>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *ObjectPtr) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *ObjectPtr) Copy() Object {
+ return o
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *ObjectPtr) IsFalsy() bool {
+ return o.Value == nil
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *ObjectPtr) Equals(x Object) bool {
+ return o == x
+}
diff --git a/vendor/github.com/d5/tengo/objects/return_value.go b/vendor/github.com/d5/tengo/objects/return_value.go
deleted file mode 100644
index f7ef1dc4..00000000
--- a/vendor/github.com/d5/tengo/objects/return_value.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package objects
-
-import "github.com/d5/tengo/compiler/token"
-
-// ReturnValue represents a value that is being returned.
-type ReturnValue struct {
- Value Object
-}
-
-// TypeName returns the name of the type.
-func (o *ReturnValue) TypeName() string {
- return "return-value"
-}
-
-func (o *ReturnValue) String() string {
- return "<return-value>"
-}
-
-// BinaryOp returns another object that is the result of
-// a given binary operator and a right-hand side object.
-func (o *ReturnValue) BinaryOp(op token.Token, rhs Object) (Object, error) {
- return nil, ErrInvalidOperator
-}
-
-// Copy returns a copy of the type.
-func (o *ReturnValue) Copy() Object {
- return &ReturnValue{Value: o.Copy()}
-}
-
-// IsFalsy returns true if the value of the type is falsy.
-func (o *ReturnValue) IsFalsy() bool {
- return false
-}
-
-// Equals returns true if the value of the type
-// is equal to the value of another object.
-func (o *ReturnValue) Equals(x Object) bool {
- return false
-}
diff --git a/vendor/github.com/d5/tengo/objects/source_module.go b/vendor/github.com/d5/tengo/objects/source_module.go
new file mode 100644
index 00000000..577fddf2
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/source_module.go
@@ -0,0 +1,11 @@
+package objects
+
+// SourceModule is an importable module that's written in Tengo.
+type SourceModule struct {
+ Src []byte
+}
+
+// Import returns a module source code.
+func (m *SourceModule) Import(_ string) (interface{}, error) {
+ return m.Src, nil
+}
diff --git a/vendor/github.com/d5/tengo/objects/user_function.go b/vendor/github.com/d5/tengo/objects/user_function.go
index 1d9bb4f7..a896788b 100644
--- a/vendor/github.com/d5/tengo/objects/user_function.go
+++ b/vendor/github.com/d5/tengo/objects/user_function.go
@@ -6,8 +6,9 @@ import (
// UserFunction represents a user function.
type UserFunction struct {
- Name string
- Value CallableFunc
+ Name string
+ Value CallableFunc
+ EncodingID string
}
// TypeName returns the name of the type.
diff --git a/vendor/github.com/d5/tengo/runtime/errors.go b/vendor/github.com/d5/tengo/runtime/errors.go
index f5f201ce..fc7ca0e9 100644
--- a/vendor/github.com/d5/tengo/runtime/errors.go
+++ b/vendor/github.com/d5/tengo/runtime/errors.go
@@ -6,3 +6,6 @@ import (
// ErrStackOverflow is a stack overflow error.
var ErrStackOverflow = errors.New("stack overflow")
+
+// ErrObjectAllocLimit is an objects allocation limit error.
+var ErrObjectAllocLimit = errors.New("object allocation limit exceeded")
diff --git a/vendor/github.com/d5/tengo/runtime/frame.go b/vendor/github.com/d5/tengo/runtime/frame.go
index 90151a1e..cbaadbdb 100644
--- a/vendor/github.com/d5/tengo/runtime/frame.go
+++ b/vendor/github.com/d5/tengo/runtime/frame.go
@@ -7,7 +7,7 @@ import (
// Frame represents a function call frame.
type Frame struct {
fn *objects.CompiledFunction
- freeVars []*objects.Object
+ freeVars []*objects.ObjectPtr
ip int
basePointer int
}
diff --git a/vendor/github.com/d5/tengo/runtime/vm.go b/vendor/github.com/d5/tengo/runtime/vm.go
index 9066bfea..dde52db5 100644
--- a/vendor/github.com/d5/tengo/runtime/vm.go
+++ b/vendor/github.com/d5/tengo/runtime/vm.go
@@ -21,73 +21,46 @@ const (
MaxFrames = 1024
)
-var (
- truePtr = &objects.TrueValue
- falsePtr = &objects.FalseValue
- undefinedPtr = &objects.UndefinedValue
-)
-
// VM is a virtual machine that executes the bytecode compiled by Compiler.
type VM struct {
- constants []objects.Object
- stack []*objects.Object
- sp int
- globals []*objects.Object
- fileSet *source.FileSet
- frames []Frame
- framesIndex int
- curFrame *Frame
- curInsts []byte
- curIPLimit int
- ip int
- aborting int64
- builtinFuncs []objects.Object
- builtinModules map[string]*objects.Object
- err error
- errOffset int
+ constants []objects.Object
+ stack [StackSize]objects.Object
+ sp int
+ globals []objects.Object
+ fileSet *source.FileSet
+ frames [MaxFrames]Frame
+ framesIndex int
+ curFrame *Frame
+ curInsts []byte
+ ip int
+ aborting int64
+ maxAllocs int64
+ allocs int64
+ err error
}
// NewVM creates a VM.
-func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object, builtinFuncs []objects.Object, builtinModules map[string]*objects.Object) *VM {
+func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, maxAllocs int64) *VM {
if globals == nil {
- globals = make([]*objects.Object, GlobalsSize)
+ globals = make([]objects.Object, GlobalsSize)
}
- if builtinModules == nil {
- builtinModules = make(map[string]*objects.Object)
+ v := &VM{
+ constants: bytecode.Constants,
+ sp: 0,
+ globals: globals,
+ fileSet: bytecode.FileSet,
+ framesIndex: 1,
+ ip: -1,
+ maxAllocs: maxAllocs,
}
- if builtinFuncs == nil {
- builtinFuncs = make([]objects.Object, len(objects.Builtins))
- for idx, fn := range objects.Builtins {
- builtinFuncs[idx] = &objects.BuiltinFunction{
- Name: fn.Name,
- Value: fn.Value,
- }
- }
- }
+ v.frames[0].fn = bytecode.MainFunction
+ v.frames[0].ip = -1
+ v.curFrame = &v.frames[0]
+ v.curInsts = v.curFrame.fn.Instructions
- frames := make([]Frame, MaxFrames)
- frames[0].fn = bytecode.MainFunction
- frames[0].freeVars = nil
- frames[0].ip = -1
- frames[0].basePointer = 0
-
- return &VM{
- constants: bytecode.Constants,
- stack: make([]*objects.Object, StackSize),
- sp: 0,
- globals: globals,
- fileSet: bytecode.FileSet,
- frames: frames,
- framesIndex: 1,
- curFrame: &(frames[0]),
- curInsts: frames[0].fn.Instructions,
- curIPLimit: len(frames[0].fn.Instructions) - 1,
- ip: -1,
- builtinFuncs: builtinFuncs,
- builtinModules: builtinModules,
- }
+ return v
}
// Abort aborts the execution.
@@ -101,109 +74,102 @@ func (v *VM) Run() (err error) {
v.sp = 0
v.curFrame = &(v.frames[0])
v.curInsts = v.curFrame.fn.Instructions
- v.curIPLimit = len(v.curInsts) - 1
v.framesIndex = 1
v.ip = -1
- atomic.StoreInt64(&v.aborting, 0)
+ v.allocs = v.maxAllocs + 1
v.run()
+ atomic.StoreInt64(&v.aborting, 0)
+
err = v.err
if err != nil {
- filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-v.errOffset])
+ filePos := v.fileSet.Position(v.curFrame.fn.SourcePos(v.ip - 1))
err = fmt.Errorf("Runtime Error: %s\n\tat %s", err.Error(), filePos)
for v.framesIndex > 1 {
v.framesIndex--
v.curFrame = &v.frames[v.framesIndex-1]
- filePos = v.fileSet.Position(v.curFrame.fn.SourceMap[v.curFrame.ip-1])
+ filePos = v.fileSet.Position(v.curFrame.fn.SourcePos(v.curFrame.ip - 1))
err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos)
}
return err
}
- // check if stack still has some objects left
- if v.sp > 0 && atomic.LoadInt64(&v.aborting) == 0 {
- panic(fmt.Errorf("non empty stack after execution: %d", v.sp))
- }
-
return nil
}
func (v *VM) run() {
-mainloop:
- for v.ip < v.curIPLimit && (atomic.LoadInt64(&v.aborting) == 0) {
+ defer func() {
+ if r := recover(); r != nil {
+ if v.sp >= StackSize || v.framesIndex >= MaxFrames {
+ v.err = ErrStackOverflow
+ return
+ }
+
+ if v.ip < len(v.curInsts)-1 {
+ if err, ok := r.(error); ok {
+ v.err = err
+ } else {
+ v.err = fmt.Errorf("panic: %v", r)
+ }
+ }
+ }
+ }()
+
+ for atomic.LoadInt64(&v.aborting) == 0 {
v.ip++
switch v.curInsts[v.ip] {
case compiler.OpConstant:
- cidx := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
+ cidx := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &v.constants[cidx]
+ v.stack[v.sp] = v.constants[cidx]
v.sp++
case compiler.OpNull:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = undefinedPtr
+ v.stack[v.sp] = objects.UndefinedValue
v.sp++
- case compiler.OpAdd:
- v.binaryOp(token.Add)
-
- case compiler.OpSub:
- v.binaryOp(token.Sub)
-
- case compiler.OpMul:
- v.binaryOp(token.Mul)
-
- case compiler.OpDiv:
- v.binaryOp(token.Quo)
-
- case compiler.OpRem:
- v.binaryOp(token.Rem)
-
- case compiler.OpBAnd:
- v.binaryOp(token.And)
+ case compiler.OpBinaryOp:
+ v.ip++
+ right := v.stack[v.sp-1]
+ left := v.stack[v.sp-2]
- case compiler.OpBOr:
- v.binaryOp(token.Or)
+ tok := token.Token(v.curInsts[v.ip])
+ res, e := left.BinaryOp(tok, right)
+ if e != nil {
+ v.sp -= 2
- case compiler.OpBXor:
- v.binaryOp(token.Xor)
+ if e == objects.ErrInvalidOperator {
+ v.err = fmt.Errorf("invalid operation: %s %s %s",
+ left.TypeName(), tok.String(), right.TypeName())
+ return
+ }
- case compiler.OpBAndNot:
- v.binaryOp(token.AndNot)
+ v.err = e
+ return
+ }
- case compiler.OpBShiftLeft:
- v.binaryOp(token.Shl)
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
- case compiler.OpBShiftRight:
- v.binaryOp(token.Shr)
+ v.stack[v.sp-2] = res
+ v.sp--
case compiler.OpEqual:
right := v.stack[v.sp-1]
left := v.stack[v.sp-2]
v.sp -= 2
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- if (*left).Equals(*right) {
- v.stack[v.sp] = truePtr
+ if left.Equals(right) {
+ v.stack[v.sp] = objects.TrueValue
} else {
- v.stack[v.sp] = falsePtr
+ v.stack[v.sp] = objects.FalseValue
}
v.sp++
@@ -212,58 +178,32 @@ mainloop:
left := v.stack[v.sp-2]
v.sp -= 2
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- if (*left).Equals(*right) {
- v.stack[v.sp] = falsePtr
+ if left.Equals(right) {
+ v.stack[v.sp] = objects.FalseValue
} else {
- v.stack[v.sp] = truePtr
+ v.stack[v.sp] = objects.TrueValue
}
v.sp++
- case compiler.OpGreaterThan:
- v.binaryOp(token.Greater)
-
- case compiler.OpGreaterThanEqual:
- v.binaryOp(token.GreaterEq)
-
case compiler.OpPop:
v.sp--
case compiler.OpTrue:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = truePtr
+ v.stack[v.sp] = objects.TrueValue
v.sp++
case compiler.OpFalse:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = falsePtr
+ v.stack[v.sp] = objects.FalseValue
v.sp++
case compiler.OpLNot:
operand := v.stack[v.sp-1]
v.sp--
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- if (*operand).IsFalsy() {
- v.stack[v.sp] = truePtr
+ if operand.IsFalsy() {
+ v.stack[v.sp] = objects.TrueValue
} else {
- v.stack[v.sp] = falsePtr
+ v.stack[v.sp] = objects.FalseValue
}
v.sp++
@@ -271,19 +211,20 @@ mainloop:
operand := v.stack[v.sp-1]
v.sp--
- switch x := (*operand).(type) {
+ switch x := operand.(type) {
case *objects.Int:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var res objects.Object = &objects.Int{Value: ^x.Value}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var res objects.Object = &objects.Int{Value: ^x.Value}
-
- v.stack[v.sp] = &res
+ v.stack[v.sp] = res
v.sp++
default:
- v.err = fmt.Errorf("invalid operation: ^%s", (*operand).TypeName())
+ v.err = fmt.Errorf("invalid operation: ^%s", operand.TypeName())
return
}
@@ -291,63 +232,60 @@ mainloop:
operand := v.stack[v.sp-1]
v.sp--
- switch x := (*operand).(type) {
+ switch x := operand.(type) {
case *objects.Int:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var res objects.Object = &objects.Int{Value: -x.Value}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var res objects.Object = &objects.Int{Value: -x.Value}
-
- v.stack[v.sp] = &res
+ v.stack[v.sp] = res
v.sp++
case *objects.Float:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var res objects.Object = &objects.Float{Value: -x.Value}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var res objects.Object = &objects.Float{Value: -x.Value}
-
- v.stack[v.sp] = &res
+ v.stack[v.sp] = res
v.sp++
default:
- v.err = fmt.Errorf("invalid operation: -%s", (*operand).TypeName())
+ v.err = fmt.Errorf("invalid operation: -%s", operand.TypeName())
return
}
case compiler.OpJumpFalsy:
- pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
-
- condition := v.stack[v.sp-1]
v.sp--
-
- if (*condition).IsFalsy() {
+ if v.stack[v.sp].IsFalsy() {
+ pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.ip = pos - 1
}
case compiler.OpAndJump:
- pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
- condition := *v.stack[v.sp-1]
- if condition.IsFalsy() {
+ if v.stack[v.sp-1].IsFalsy() {
+ pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.ip = pos - 1
} else {
v.sp--
}
case compiler.OpOrJump:
- pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
- condition := *v.stack[v.sp-1]
- if !condition.IsFalsy() {
- v.ip = pos - 1
- } else {
+ if v.stack[v.sp-1].IsFalsy() {
v.sp--
+ } else {
+ pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
+ v.ip = pos - 1
}
case compiler.OpJump:
@@ -355,108 +293,127 @@ mainloop:
v.ip = pos - 1
case compiler.OpSetGlobal:
- globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
-
v.sp--
+ globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.globals[globalIndex] = v.stack[v.sp]
case compiler.OpSetSelGlobal:
- globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
- numSelectors := int(v.curInsts[v.ip+3])
v.ip += 3
+ globalIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8
+ numSelectors := int(v.curInsts[v.ip])
// selectors and RHS value
- selectors := v.stack[v.sp-numSelectors : v.sp]
+ selectors := make([]objects.Object, numSelectors)
+ for i := 0; i < numSelectors; i++ {
+ selectors[i] = v.stack[v.sp-numSelectors+i]
+ }
+
val := v.stack[v.sp-numSelectors-1]
v.sp -= numSelectors + 1
if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil {
- v.errOffset = 3
v.err = e
return
}
case compiler.OpGetGlobal:
- globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
+ globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
val := v.globals[globalIndex]
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
v.stack[v.sp] = val
v.sp++
case compiler.OpArray:
- numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
+ numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
var elements []objects.Object
for i := v.sp - numElements; i < v.sp; i++ {
- elements = append(elements, *v.stack[i])
+ elements = append(elements, v.stack[i])
}
v.sp -= numElements
var arr objects.Object = &objects.Array{Value: elements}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- v.stack[v.sp] = &arr
+ v.stack[v.sp] = arr
v.sp++
case compiler.OpMap:
- numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
+ numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
kv := make(map[string]objects.Object)
for i := v.sp - numElements; i < v.sp; i += 2 {
- key := *v.stack[i]
- value := *v.stack[i+1]
+ key := v.stack[i]
+ value := v.stack[i+1]
kv[key.(*objects.String).Value] = value
}
v.sp -= numElements
var m objects.Object = &objects.Map{Value: kv}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- v.stack[v.sp] = &m
+ v.stack[v.sp] = m
v.sp++
case compiler.OpError:
value := v.stack[v.sp-1]
var e objects.Object = &objects.Error{
- Value: *value,
+ Value: value,
}
- v.stack[v.sp-1] = &e
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
+
+ v.stack[v.sp-1] = e
case compiler.OpImmutable:
value := v.stack[v.sp-1]
- switch value := (*value).(type) {
+ switch value := value.(type) {
case *objects.Array:
var immutableArray objects.Object = &objects.ImmutableArray{
Value: value.Value,
}
- v.stack[v.sp-1] = &immutableArray
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
+
+ v.stack[v.sp-1] = immutableArray
case *objects.Map:
var immutableMap objects.Object = &objects.ImmutableMap{
Value: value.Value,
}
- v.stack[v.sp-1] = &immutableMap
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
+
+ v.stack[v.sp-1] = immutableMap
}
case compiler.OpIndex:
@@ -464,13 +421,13 @@ mainloop:
left := v.stack[v.sp-2]
v.sp -= 2
- switch left := (*left).(type) {
+ switch left := left.(type) {
case objects.Indexable:
- val, e := left.IndexGet(*index)
+ val, e := left.IndexGet(index)
if e != nil {
if e == objects.ErrInvalidIndexType {
- v.err = fmt.Errorf("invalid index type: %s", (*index).TypeName())
+ v.err = fmt.Errorf("invalid index type: %s", index.TypeName())
return
}
@@ -481,27 +438,17 @@ mainloop:
val = objects.UndefinedValue
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case *objects.Error: // e.value
- key, ok := (*index).(*objects.String)
+ key, ok := index.(*objects.String)
if !ok || key.Value != "value" {
v.err = fmt.Errorf("invalid index on error")
return
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &left.Value
+ v.stack[v.sp] = left.Value
v.sp++
default:
@@ -516,8 +463,8 @@ mainloop:
v.sp -= 3
var lowIdx int64
- if *low != objects.UndefinedValue {
- if low, ok := (*low).(*objects.Int); ok {
+ if low != objects.UndefinedValue {
+ if low, ok := low.(*objects.Int); ok {
lowIdx = low.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", low.TypeName())
@@ -525,13 +472,13 @@ mainloop:
}
}
- switch left := (*left).(type) {
+ switch left := left.(type) {
case *objects.Array:
numElements := int64(len(left.Value))
var highIdx int64
- if *high == objects.UndefinedValue {
+ if high == objects.UndefinedValue {
highIdx = numElements
- } else if high, ok := (*high).(*objects.Int); ok {
+ } else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
@@ -555,21 +502,23 @@ mainloop:
highIdx = numElements
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case *objects.ImmutableArray:
numElements := int64(len(left.Value))
var highIdx int64
- if *high == objects.UndefinedValue {
+ if high == objects.UndefinedValue {
highIdx = numElements
- } else if high, ok := (*high).(*objects.Int); ok {
+ } else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
@@ -593,22 +542,23 @@ mainloop:
highIdx = numElements
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case *objects.String:
numElements := int64(len(left.Value))
var highIdx int64
- if *high == objects.UndefinedValue {
+ if high == objects.UndefinedValue {
highIdx = numElements
- } else if high, ok := (*high).(*objects.Int); ok {
+ } else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
@@ -632,22 +582,23 @@ mainloop:
highIdx = numElements
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]}
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case *objects.Bytes:
numElements := int64(len(left.Value))
var highIdx int64
- if *high == objects.UndefinedValue {
+ if high == objects.UndefinedValue {
highIdx = numElements
- } else if high, ok := (*high).(*objects.Int); ok {
+ } else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
@@ -671,14 +622,15 @@ mainloop:
highIdx = numElements
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]}
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
}
@@ -686,12 +638,11 @@ mainloop:
numArgs := int(v.curInsts[v.ip+1])
v.ip++
- value := *v.stack[v.sp-1-numArgs]
+ value := v.stack[v.sp-1-numArgs]
switch callee := value.(type) {
case *objects.Closure:
if numArgs != callee.Fn.NumParameters {
- v.errOffset = 1
v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d",
callee.Fn.NumParameters, numArgs)
return
@@ -700,14 +651,14 @@ mainloop:
// test if it's tail-call
if callee.Fn == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1]
- if nextOp == compiler.OpReturnValue ||
+ if nextOp == compiler.OpReturn ||
(nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
for p := 0; p < numArgs; p++ {
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
}
v.sp -= numArgs + 1
v.ip = -1 // reset IP to beginning of the frame
- continue mainloop
+ continue
}
}
@@ -719,13 +670,11 @@ mainloop:
v.curFrame.basePointer = v.sp - numArgs
v.curInsts = callee.Fn.Instructions
v.ip = -1
- v.curIPLimit = len(v.curInsts) - 1
v.framesIndex++
v.sp = v.sp - numArgs + callee.Fn.NumLocals
case *objects.CompiledFunction:
if numArgs != callee.NumParameters {
- v.errOffset = 1
v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d",
callee.NumParameters, numArgs)
return
@@ -734,14 +683,14 @@ mainloop:
// test if it's tail-call
if callee == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1]
- if nextOp == compiler.OpReturnValue ||
+ if nextOp == compiler.OpReturn ||
(nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
for p := 0; p < numArgs; p++ {
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
}
v.sp -= numArgs + 1
v.ip = -1 // reset IP to beginning of the frame
- continue mainloop
+ continue
}
}
@@ -753,14 +702,13 @@ mainloop:
v.curFrame.basePointer = v.sp - numArgs
v.curInsts = callee.Instructions
v.ip = -1
- v.curIPLimit = len(v.curInsts) - 1
v.framesIndex++
v.sp = v.sp - numArgs + callee.NumLocals
case objects.Callable:
var args []objects.Object
for _, arg := range v.stack[v.sp-numArgs : v.sp] {
- args = append(args, *arg)
+ args = append(args, arg)
}
ret, e := callee.Call(args...)
@@ -768,8 +716,6 @@ mainloop:
// runtime error
if e != nil {
- v.errOffset = 1
-
if e == objects.ErrWrongNumArguments {
v.err = fmt.Errorf("wrong number of arguments in call to '%s'",
value.TypeName())
@@ -791,65 +737,54 @@ mainloop:
ret = objects.UndefinedValue
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- v.stack[v.sp] = &ret
+ v.stack[v.sp] = ret
v.sp++
default:
- v.errOffset = 1
v.err = fmt.Errorf("not callable: %s", callee.TypeName())
return
}
- case compiler.OpReturnValue:
- retVal := v.stack[v.sp-1]
+ case compiler.OpReturn:
+ v.ip++
+ var retVal objects.Object
+ if int(v.curInsts[v.ip]) == 1 {
+ retVal = v.stack[v.sp-1]
+ } else {
+ retVal = objects.UndefinedValue
+ }
//v.sp--
v.framesIndex--
- lastFrame := v.frames[v.framesIndex]
v.curFrame = &v.frames[v.framesIndex-1]
v.curInsts = v.curFrame.fn.Instructions
- v.curIPLimit = len(v.curInsts) - 1
v.ip = v.curFrame.ip
//v.sp = lastFrame.basePointer - 1
- v.sp = lastFrame.basePointer
+ v.sp = v.frames[v.framesIndex].basePointer
// skip stack overflow check because (newSP) <= (oldSP)
v.stack[v.sp-1] = retVal
//v.sp++
- case compiler.OpReturn:
- v.framesIndex--
- lastFrame := v.frames[v.framesIndex]
- v.curFrame = &v.frames[v.framesIndex-1]
- v.curInsts = v.curFrame.fn.Instructions
- v.curIPLimit = len(v.curInsts) - 1
- v.ip = v.curFrame.ip
-
- //v.sp = lastFrame.basePointer - 1
- v.sp = lastFrame.basePointer
-
- // skip stack overflow check because (newSP) <= (oldSP)
- v.stack[v.sp-1] = undefinedPtr
- //v.sp++
-
case compiler.OpDefineLocal:
- localIndex := int(v.curInsts[v.ip+1])
v.ip++
+ localIndex := int(v.curInsts[v.ip])
sp := v.curFrame.basePointer + localIndex
// local variables can be mutated by other actions
// so always store the copy of popped value
- val := *v.stack[v.sp-1]
+ val := v.stack[v.sp-1]
v.sp--
- v.stack[sp] = &val
+ v.stack[sp] = val
case compiler.OpSetLocal:
localIndex := int(v.curInsts[v.ip+1])
@@ -862,7 +797,11 @@ mainloop:
val := v.stack[v.sp-1]
v.sp--
- *v.stack[sp] = *val // also use a copy of popped value
+ if obj, ok := v.stack[sp].(*objects.ObjectPtr); ok {
+ *obj.Value = val
+ val = obj
+ }
+ v.stack[sp] = val // also use a copy of popped value
case compiler.OpSetSelLocal:
localIndex := int(v.curInsts[v.ip+1])
@@ -870,172 +809,173 @@ mainloop:
v.ip += 2
// selectors and RHS value
- selectors := v.stack[v.sp-numSelectors : v.sp]
+ selectors := make([]objects.Object, numSelectors)
+ for i := 0; i < numSelectors; i++ {
+ selectors[i] = v.stack[v.sp-numSelectors+i]
+ }
+
val := v.stack[v.sp-numSelectors-1]
v.sp -= numSelectors + 1
sp := v.curFrame.basePointer + localIndex
if e := indexAssign(v.stack[sp], val, selectors); e != nil {
- v.errOffset = 2
v.err = e
return
}
case compiler.OpGetLocal:
- localIndex := int(v.curInsts[v.ip+1])
v.ip++
+ localIndex := int(v.curInsts[v.ip])
val := v.stack[v.curFrame.basePointer+localIndex]
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
+ if obj, ok := val.(*objects.ObjectPtr); ok {
+ val = *obj.Value
}
v.stack[v.sp] = val
v.sp++
case compiler.OpGetBuiltin:
- builtinIndex := int(v.curInsts[v.ip+1])
v.ip++
+ builtinIndex := int(v.curInsts[v.ip])
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &v.builtinFuncs[builtinIndex]
- v.sp++
-
- case compiler.OpGetBuiltinModule:
- val := v.stack[v.sp-1]
- v.sp--
-
- moduleName := (*val).(*objects.String).Value
-
- module, ok := v.builtinModules[moduleName]
- if !ok {
- v.errOffset = 3
- v.err = fmt.Errorf("module '%s' not found", moduleName)
- return
- }
-
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = module
+ v.stack[v.sp] = objects.Builtins[builtinIndex]
v.sp++
case compiler.OpClosure:
- constIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
- numFree := int(v.curInsts[v.ip+3])
v.ip += 3
+ constIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8
+ numFree := int(v.curInsts[v.ip])
fn, ok := v.constants[constIndex].(*objects.CompiledFunction)
if !ok {
- v.errOffset = 3
v.err = fmt.Errorf("not function: %s", fn.TypeName())
return
}
- free := make([]*objects.Object, numFree)
+ free := make([]*objects.ObjectPtr, numFree)
for i := 0; i < numFree; i++ {
- free[i] = v.stack[v.sp-numFree+i]
+ switch freeVar := (v.stack[v.sp-numFree+i]).(type) {
+ case *objects.ObjectPtr:
+ free[i] = freeVar
+ default:
+ free[i] = &objects.ObjectPtr{Value: &v.stack[v.sp-numFree+i]}
+ }
}
- v.sp -= numFree
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
+ v.sp -= numFree
- var cl objects.Object = &objects.Closure{
+ var cl = &objects.Closure{
Fn: fn,
Free: free,
}
- v.stack[v.sp] = &cl
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
+
+ v.stack[v.sp] = cl
v.sp++
- case compiler.OpGetFree:
- freeIndex := int(v.curInsts[v.ip+1])
+ case compiler.OpGetFreePtr:
v.ip++
+ freeIndex := int(v.curInsts[v.ip])
val := v.curFrame.freeVars[freeIndex]
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
+ v.stack[v.sp] = val
+ v.sp++
+
+ case compiler.OpGetFree:
+ v.ip++
+ freeIndex := int(v.curInsts[v.ip])
+
+ val := *v.curFrame.freeVars[freeIndex].Value
v.stack[v.sp] = val
v.sp++
+ case compiler.OpSetFree:
+ v.ip++
+ freeIndex := int(v.curInsts[v.ip])
+
+ *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1]
+
+ v.sp--
+
+ case compiler.OpGetLocalPtr:
+ v.ip++
+ localIndex := int(v.curInsts[v.ip])
+
+ sp := v.curFrame.basePointer + localIndex
+ val := v.stack[sp]
+
+ var freeVar *objects.ObjectPtr
+ if obj, ok := val.(*objects.ObjectPtr); ok {
+ freeVar = obj
+ } else {
+ freeVar = &objects.ObjectPtr{Value: &val}
+ v.stack[sp] = freeVar
+ }
+
+ v.stack[v.sp] = freeVar
+ v.sp++
+
case compiler.OpSetSelFree:
- freeIndex := int(v.curInsts[v.ip+1])
- numSelectors := int(v.curInsts[v.ip+2])
v.ip += 2
+ freeIndex := int(v.curInsts[v.ip-1])
+ numSelectors := int(v.curInsts[v.ip])
// selectors and RHS value
- selectors := v.stack[v.sp-numSelectors : v.sp]
+ selectors := make([]objects.Object, numSelectors)
+ for i := 0; i < numSelectors; i++ {
+ selectors[i] = v.stack[v.sp-numSelectors+i]
+ }
val := v.stack[v.sp-numSelectors-1]
v.sp -= numSelectors + 1
- if e := indexAssign(v.curFrame.freeVars[freeIndex], val, selectors); e != nil {
- v.errOffset = 2
+ if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil {
v.err = e
return
}
- case compiler.OpSetFree:
- freeIndex := int(v.curInsts[v.ip+1])
- v.ip++
-
- val := v.stack[v.sp-1]
- v.sp--
-
- *v.curFrame.freeVars[freeIndex] = *val
-
case compiler.OpIteratorInit:
var iterator objects.Object
dst := v.stack[v.sp-1]
v.sp--
- iterable, ok := (*dst).(objects.Iterable)
+ iterable, ok := dst.(objects.Iterable)
if !ok {
- v.err = fmt.Errorf("not iterable: %s", (*dst).TypeName())
+ v.err = fmt.Errorf("not iterable: %s", dst.TypeName())
return
}
iterator = iterable.Iterate()
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- v.stack[v.sp] = &iterator
+ v.stack[v.sp] = iterator
v.sp++
case compiler.OpIteratorNext:
iterator := v.stack[v.sp-1]
v.sp--
- hasMore := (*iterator).(objects.Iterator).Next()
-
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
+ hasMore := iterator.(objects.Iterator).Next()
if hasMore {
- v.stack[v.sp] = truePtr
+ v.stack[v.sp] = objects.TrueValue
} else {
- v.stack[v.sp] = falsePtr
+ v.stack[v.sp] = objects.FalseValue
}
v.sp++
@@ -1043,70 +983,61 @@ mainloop:
iterator := v.stack[v.sp-1]
v.sp--
- val := (*iterator).(objects.Iterator).Key()
+ val := iterator.(objects.Iterator).Key()
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case compiler.OpIteratorValue:
iterator := v.stack[v.sp-1]
v.sp--
- val := (*iterator).(objects.Iterator).Value()
+ val := iterator.(objects.Iterator).Value()
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
default:
- panic(fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip]))
+ v.err = fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip])
+ return
}
}
}
-// Globals returns the global variables.
-func (v *VM) Globals() []*objects.Object {
- return v.globals
+// IsStackEmpty tests if the stack is empty or not.
+func (v *VM) IsStackEmpty() bool {
+ return v.sp == 0
}
-func indexAssign(dst, src *objects.Object, selectors []*objects.Object) error {
+func indexAssign(dst, src objects.Object, selectors []objects.Object) error {
numSel := len(selectors)
for sidx := numSel - 1; sidx > 0; sidx-- {
- indexable, ok := (*dst).(objects.Indexable)
+ indexable, ok := dst.(objects.Indexable)
if !ok {
- return fmt.Errorf("not indexable: %s", (*dst).TypeName())
+ return fmt.Errorf("not indexable: %s", dst.TypeName())
}
- next, err := indexable.IndexGet(*selectors[sidx])
+ next, err := indexable.IndexGet(selectors[sidx])
if err != nil {
if err == objects.ErrInvalidIndexType {
- return fmt.Errorf("invalid index type: %s", (*selectors[sidx]).TypeName())
+ return fmt.Errorf("invalid index type: %s", selectors[sidx].TypeName())
}
return err
}
- dst = &next
+ dst = next
}
- indexAssignable, ok := (*dst).(objects.IndexAssignable)
+ indexAssignable, ok := dst.(objects.IndexAssignable)
if !ok {
- return fmt.Errorf("not index-assignable: %s", (*dst).TypeName())
+ return fmt.Errorf("not index-assignable: %s", dst.TypeName())
}
- if err := indexAssignable.IndexSet(*selectors[0], *src); err != nil {
+ if err := indexAssignable.IndexSet(selectors[0], src); err != nil {
if err == objects.ErrInvalidIndexValueType {
- return fmt.Errorf("invaid index value type: %s", (*src).TypeName())
+ return fmt.Errorf("invaid index value type: %s", src.TypeName())
}
return err
@@ -1114,26 +1045,3 @@ func indexAssign(dst, src *objects.Object, selectors []*objects.Object) error {
return nil
}
-
-func (v *VM) binaryOp(tok token.Token) {
- right := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
-
- res, e := (*left).BinaryOp(tok, *right)
- if e != nil {
- v.sp -= 2
- atomic.StoreInt64(&v.aborting, 1)
-
- if e == objects.ErrInvalidOperator {
- v.err = fmt.Errorf("invalid operation: %s + %s",
- (*left).TypeName(), (*right).TypeName())
- return
- }
-
- v.err = e
- return
- }
-
- v.stack[v.sp-2] = &res
- v.sp--
-}
diff --git a/vendor/github.com/d5/tengo/script/compiled.go b/vendor/github.com/d5/tengo/script/compiled.go
index 4acc46ee..ce50f498 100644
--- a/vendor/github.com/d5/tengo/script/compiled.go
+++ b/vendor/github.com/d5/tengo/script/compiled.go
@@ -3,6 +3,7 @@ package script
import (
"context"
"fmt"
+ "sync"
"github.com/d5/tengo/compiler"
"github.com/d5/tengo/objects"
@@ -12,26 +13,39 @@ import (
// Compiled is a compiled instance of the user script.
// Use Script.Compile() to create Compiled object.
type Compiled struct {
- symbolTable *compiler.SymbolTable
- machine *runtime.VM
+ globalIndexes map[string]int // global symbol name to index
+ bytecode *compiler.Bytecode
+ globals []objects.Object
+ maxAllocs int64
+ lock sync.RWMutex
}
// Run executes the compiled script in the virtual machine.
func (c *Compiled) Run() error {
- return c.machine.Run()
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ v := runtime.NewVM(c.bytecode, c.globals, c.maxAllocs)
+
+ return v.Run()
}
// RunContext is like Run but includes a context.
func (c *Compiled) RunContext(ctx context.Context) (err error) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ v := runtime.NewVM(c.bytecode, c.globals, c.maxAllocs)
+
ch := make(chan error, 1)
go func() {
- ch <- c.machine.Run()
+ ch <- v.Run()
}()
select {
case <-ctx.Done():
- c.machine.Abort()
+ v.Abort()
<-ch
err = ctx.Err()
case err = <-ch:
@@ -40,30 +54,58 @@ func (c *Compiled) RunContext(ctx context.Context) (err error) {
return
}
+// Clone creates a new copy of Compiled.
+// Cloned copies are safe for concurrent use by multiple goroutines.
+func (c *Compiled) Clone() *Compiled {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ clone := &Compiled{
+ globalIndexes: c.globalIndexes,
+ bytecode: c.bytecode,
+ globals: make([]objects.Object, len(c.globals)),
+ maxAllocs: c.maxAllocs,
+ }
+
+ // copy global objects
+ for idx, g := range c.globals {
+ if g != nil {
+ clone.globals[idx] = g
+ }
+ }
+
+ return clone
+}
+
// IsDefined returns true if the variable name is defined (has value) before or after the execution.
func (c *Compiled) IsDefined(name string) bool {
- symbol, _, ok := c.symbolTable.Resolve(name)
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+
+ idx, ok := c.globalIndexes[name]
if !ok {
return false
}
- v := c.machine.Globals()[symbol.Index]
+ v := c.globals[idx]
if v == nil {
return false
}
- return *v != objects.UndefinedValue
+ return v != objects.UndefinedValue
}
// Get returns a variable identified by the name.
func (c *Compiled) Get(name string) *Variable {
- value := &objects.UndefinedValue
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+
+ value := objects.UndefinedValue
- symbol, _, ok := c.symbolTable.Resolve(name)
- if ok && symbol.Scope == compiler.ScopeGlobal {
- value = c.machine.Globals()[symbol.Index]
+ if idx, ok := c.globalIndexes[name]; ok {
+ value = c.globals[idx]
if value == nil {
- value = &objects.UndefinedValue
+ value = objects.UndefinedValue
}
}
@@ -75,20 +117,21 @@ func (c *Compiled) Get(name string) *Variable {
// GetAll returns all the variables that are defined by the compiled script.
func (c *Compiled) GetAll() []*Variable {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+
var vars []*Variable
- for _, name := range c.symbolTable.Names() {
- symbol, _, ok := c.symbolTable.Resolve(name)
- if ok && symbol.Scope == compiler.ScopeGlobal {
- value := c.machine.Globals()[symbol.Index]
- if value == nil {
- value = &objects.UndefinedValue
- }
-
- vars = append(vars, &Variable{
- name: name,
- value: value,
- })
+
+ for name, idx := range c.globalIndexes {
+ value := c.globals[idx]
+ if value == nil {
+ value = objects.UndefinedValue
}
+
+ vars = append(vars, &Variable{
+ name: name,
+ value: value,
+ })
}
return vars
@@ -97,17 +140,20 @@ func (c *Compiled) GetAll() []*Variable {
// Set replaces the value of a global variable identified by the name.
// An error will be returned if the name was not defined during compilation.
func (c *Compiled) Set(name string, value interface{}) error {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
obj, err := objects.FromInterface(value)
if err != nil {
return err
}
- symbol, _, ok := c.symbolTable.Resolve(name)
- if !ok || symbol.Scope != compiler.ScopeGlobal {
+ idx, ok := c.globalIndexes[name]
+ if !ok {
return fmt.Errorf("'%s' is not defined", name)
}
- c.machine.Globals()[symbol.Index] = &obj
+ c.globals[idx] = obj
return nil
}
diff --git a/vendor/github.com/d5/tengo/script/conversion.go b/vendor/github.com/d5/tengo/script/conversion.go
deleted file mode 100644
index c35c1411..00000000
--- a/vendor/github.com/d5/tengo/script/conversion.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package script
-
-import (
- "github.com/d5/tengo/objects"
-)
-
-func objectToInterface(o objects.Object) interface{} {
- switch val := o.(type) {
- case *objects.Array:
- return val.Value
- case *objects.Map:
- return val.Value
- case *objects.Int:
- return val.Value
- case *objects.Float:
- return val.Value
- case *objects.Bool:
- if val == objects.TrueValue {
- return true
- }
- return false
- case *objects.Char:
- return val.Value
- case *objects.String:
- return val.Value
- case *objects.Bytes:
- return val.Value
- case *objects.Undefined:
- return nil
- }
-
- return o
-}
diff --git a/vendor/github.com/d5/tengo/script/script.go b/vendor/github.com/d5/tengo/script/script.go
index e8db52a3..cdf36713 100644
--- a/vendor/github.com/d5/tengo/script/script.go
+++ b/vendor/github.com/d5/tengo/script/script.go
@@ -14,17 +14,20 @@ import (
// Script can simplify compilation and execution of embedded scripts.
type Script struct {
variables map[string]*Variable
- builtinFuncs []objects.Object
- builtinModules map[string]*objects.Object
- userModuleLoader compiler.ModuleLoader
+ modules *objects.ModuleMap
input []byte
+ maxAllocs int64
+ maxConstObjects int
+ enableFileImport bool
}
// New creates a Script instance with an input script.
func New(input []byte) *Script {
return &Script{
- variables: make(map[string]*Variable),
- input: input,
+ variables: make(map[string]*Variable),
+ input: input,
+ maxAllocs: -1,
+ maxConstObjects: -1,
}
}
@@ -37,7 +40,7 @@ func (s *Script) Add(name string, value interface{}) error {
s.variables[name] = &Variable{
name: name,
- value: &obj,
+ value: obj,
}
return nil
@@ -55,38 +58,31 @@ func (s *Script) Remove(name string) bool {
return true
}
-// SetBuiltinFunctions allows to define builtin functions.
-func (s *Script) SetBuiltinFunctions(funcs []*objects.BuiltinFunction) {
- if funcs != nil {
- s.builtinFuncs = make([]objects.Object, len(funcs))
- for idx, fn := range funcs {
- s.builtinFuncs[idx] = fn
- }
- } else {
- s.builtinFuncs = []objects.Object{}
- }
+// SetImports sets import modules.
+func (s *Script) SetImports(modules *objects.ModuleMap) {
+ s.modules = modules
}
-// SetBuiltinModules allows to define builtin modules.
-func (s *Script) SetBuiltinModules(modules map[string]*objects.ImmutableMap) {
- if modules != nil {
- s.builtinModules = make(map[string]*objects.Object, len(modules))
- for k, mod := range modules {
- s.builtinModules[k] = objectPtr(mod)
- }
- } else {
- s.builtinModules = map[string]*objects.Object{}
- }
+// SetMaxAllocs sets the maximum number of objects allocations during the run time.
+// Compiled script will return runtime.ErrObjectAllocLimit error if it exceeds this limit.
+func (s *Script) SetMaxAllocs(n int64) {
+ s.maxAllocs = n
}
-// SetUserModuleLoader sets the user module loader for the compiler.
-func (s *Script) SetUserModuleLoader(loader compiler.ModuleLoader) {
- s.userModuleLoader = loader
+// SetMaxConstObjects sets the maximum number of objects in the compiled constants.
+func (s *Script) SetMaxConstObjects(n int) {
+ s.maxConstObjects = n
+}
+
+// EnableFileImport enables or disables module loading from local files.
+// Local file modules are disabled by default.
+func (s *Script) EnableFileImport(enable bool) {
+ s.enableFileImport = enable
}
// Compile compiles the script with all the defined variables, and, returns Compiled object.
func (s *Script) Compile() (*Compiled, error) {
- symbolTable, builtinModules, globals, err := s.prepCompile()
+ symbolTable, globals, err := s.prepCompile()
if err != nil {
return nil, err
}
@@ -100,19 +96,41 @@ func (s *Script) Compile() (*Compiled, error) {
return nil, err
}
- c := compiler.NewCompiler(srcFile, symbolTable, nil, builtinModules, nil)
+ c := compiler.NewCompiler(srcFile, symbolTable, nil, s.modules, nil)
+ c.EnableFileImport(s.enableFileImport)
+ if err := c.Compile(file); err != nil {
+ return nil, err
+ }
+
+ // reduce globals size
+ globals = globals[:symbolTable.MaxSymbols()+1]
- if s.userModuleLoader != nil {
- c.SetModuleLoader(s.userModuleLoader)
+ // global symbol names to indexes
+ globalIndexes := make(map[string]int, len(globals))
+ for _, name := range symbolTable.Names() {
+ symbol, _, _ := symbolTable.Resolve(name)
+ if symbol.Scope == compiler.ScopeGlobal {
+ globalIndexes[name] = symbol.Index
+ }
}
- if err := c.Compile(file); err != nil {
- return nil, err
+ // remove duplicates from constants
+ bytecode := c.Bytecode()
+ bytecode.RemoveDuplicates()
+
+ // check the constant objects limit
+ if s.maxConstObjects >= 0 {
+ cnt := bytecode.CountObjects()
+ if cnt > s.maxConstObjects {
+ return nil, fmt.Errorf("exceeding constant objects limit: %d", cnt)
+ }
}
return &Compiled{
- symbolTable: symbolTable,
- machine: runtime.NewVM(c.Bytecode(), globals, s.builtinFuncs, s.builtinModules),
+ globalIndexes: globalIndexes,
+ bytecode: bytecode,
+ globals: globals,
+ maxAllocs: s.maxAllocs,
}, nil
}
@@ -141,39 +159,18 @@ func (s *Script) RunContext(ctx context.Context) (compiled *Compiled, err error)
return
}
-func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, builtinModules map[string]bool, globals []*objects.Object, err error) {
+func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, globals []objects.Object, err error) {
var names []string
for name := range s.variables {
names = append(names, name)
}
symbolTable = compiler.NewSymbolTable()
-
- if s.builtinFuncs == nil {
- s.builtinFuncs = make([]objects.Object, len(objects.Builtins))
- for idx, fn := range objects.Builtins {
- s.builtinFuncs[idx] = &objects.BuiltinFunction{
- Name: fn.Name,
- Value: fn.Value,
- }
- }
- }
-
- if s.builtinModules == nil {
- s.builtinModules = make(map[string]*objects.Object)
+ for idx, fn := range objects.Builtins {
+ symbolTable.DefineBuiltin(idx, fn.Name)
}
- for idx, fn := range s.builtinFuncs {
- f := fn.(*objects.BuiltinFunction)
- symbolTable.DefineBuiltin(idx, f.Name)
- }
-
- builtinModules = make(map[string]bool)
- for name := range s.builtinModules {
- builtinModules[name] = true
- }
-
- globals = make([]*objects.Object, runtime.GlobalsSize, runtime.GlobalsSize)
+ globals = make([]objects.Object, runtime.GlobalsSize)
for idx, name := range names {
symbol := symbolTable.Define(name)
@@ -195,7 +192,3 @@ func (s *Script) copyVariables() map[string]*Variable {
return vars
}
-
-func objectPtr(o objects.Object) *objects.Object {
- return &o
-}
diff --git a/vendor/github.com/d5/tengo/script/variable.go b/vendor/github.com/d5/tengo/script/variable.go
index c5e01bd9..df345115 100644
--- a/vendor/github.com/d5/tengo/script/variable.go
+++ b/vendor/github.com/d5/tengo/script/variable.go
@@ -9,7 +9,7 @@ import (
// Variable is a user-defined variable for the script.
type Variable struct {
name string
- value *objects.Object
+ value objects.Object
}
// NewVariable creates a Variable.
@@ -21,7 +21,7 @@ func NewVariable(name string, value interface{}) (*Variable, error) {
return &Variable{
name: name,
- value: &obj,
+ value: obj,
}, nil
}
@@ -32,18 +32,18 @@ func (v *Variable) Name() string {
// Value returns an empty interface of the variable value.
func (v *Variable) Value() interface{} {
- return objectToInterface(*v.value)
+ return objects.ToInterface(v.value)
}
// ValueType returns the name of the value type.
func (v *Variable) ValueType() string {
- return (*v.value).TypeName()
+ return v.value.TypeName()
}
// Int returns int value of the variable value.
// It returns 0 if the value is not convertible to int.
func (v *Variable) Int() int {
- c, _ := objects.ToInt(*v.value)
+ c, _ := objects.ToInt(v.value)
return c
}
@@ -51,7 +51,7 @@ func (v *Variable) Int() int {
// Int64 returns int64 value of the variable value.
// It returns 0 if the value is not convertible to int64.
func (v *Variable) Int64() int64 {
- c, _ := objects.ToInt64(*v.value)
+ c, _ := objects.ToInt64(v.value)
return c
}
@@ -59,7 +59,7 @@ func (v *Variable) Int64() int64 {
// Float returns float64 value of the variable value.
// It returns 0.0 if the value is not convertible to float64.
func (v *Variable) Float() float64 {
- c, _ := objects.ToFloat64(*v.value)
+ c, _ := objects.ToFloat64(v.value)
return c
}
@@ -67,7 +67,7 @@ func (v *Variable) Float() float64 {
// Char returns rune value of the variable value.
// It returns 0 if the value is not convertible to rune.
func (v *Variable) Char() rune {
- c, _ := objects.ToRune(*v.value)
+ c, _ := objects.ToRune(v.value)
return c
}
@@ -75,7 +75,7 @@ func (v *Variable) Char() rune {
// Bool returns bool value of the variable value.
// It returns 0 if the value is not convertible to bool.
func (v *Variable) Bool() bool {
- c, _ := objects.ToBool(*v.value)
+ c, _ := objects.ToBool(v.value)
return c
}
@@ -83,11 +83,11 @@ func (v *Variable) Bool() bool {
// Array returns []interface value of the variable value.
// It returns 0 if the value is not convertible to []interface.
func (v *Variable) Array() []interface{} {
- switch val := (*v.value).(type) {
+ switch val := v.value.(type) {
case *objects.Array:
var arr []interface{}
for _, e := range val.Value {
- arr = append(arr, objectToInterface(e))
+ arr = append(arr, objects.ToInterface(e))
}
return arr
}
@@ -98,11 +98,11 @@ func (v *Variable) Array() []interface{} {
// Map returns map[string]interface{} value of the variable value.
// It returns 0 if the value is not convertible to map[string]interface{}.
func (v *Variable) Map() map[string]interface{} {
- switch val := (*v.value).(type) {
+ switch val := v.value.(type) {
case *objects.Map:
kv := make(map[string]interface{})
for mk, mv := range val.Value {
- kv[mk] = objectToInterface(mv)
+ kv[mk] = objects.ToInterface(mv)
}
return kv
}
@@ -113,7 +113,7 @@ func (v *Variable) Map() map[string]interface{} {
// String returns string value of the variable value.
// It returns 0 if the value is not convertible to string.
func (v *Variable) String() string {
- c, _ := objects.ToString(*v.value)
+ c, _ := objects.ToString(v.value)
return c
}
@@ -121,7 +121,7 @@ func (v *Variable) String() string {
// Bytes returns a byte slice of the variable value.
// It returns nil if the value is not convertible to byte slice.
func (v *Variable) Bytes() []byte {
- c, _ := objects.ToByteSlice(*v.value)
+ c, _ := objects.ToByteSlice(v.value)
return c
}
@@ -129,7 +129,7 @@ func (v *Variable) Bytes() []byte {
// Error returns an error if the underlying value is error object.
// If not, this returns nil.
func (v *Variable) Error() error {
- err, ok := (*v.value).(*objects.Error)
+ err, ok := v.value.(*objects.Error)
if ok {
return errors.New(err.String())
}
@@ -140,10 +140,10 @@ func (v *Variable) Error() error {
// Object returns an underlying Object of the variable value.
// Note that returned Object is a copy of an actual Object used in the script.
func (v *Variable) Object() objects.Object {
- return *v.value
+ return v.value
}
// IsUndefined returns true if the underlying value is undefined.
func (v *Variable) IsUndefined() bool {
- return *v.value == objects.UndefinedValue
+ return v.value == objects.UndefinedValue
}
diff --git a/vendor/github.com/d5/tengo/stdlib/builtin_modules.go b/vendor/github.com/d5/tengo/stdlib/builtin_modules.go
new file mode 100644
index 00000000..cc2796f9
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/builtin_modules.go
@@ -0,0 +1,14 @@
+package stdlib
+
+import "github.com/d5/tengo/objects"
+
+// BuiltinModules are builtin type standard library modules.
+var BuiltinModules = map[string]map[string]objects.Object{
+ "math": mathModule,
+ "os": osModule,
+ "text": textModule,
+ "times": timesModule,
+ "rand": randModule,
+ "fmt": fmtModule,
+ "json": jsonModule,
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/errors.go b/vendor/github.com/d5/tengo/stdlib/errors.go
new file mode 100644
index 00000000..a2942bb0
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/errors.go
@@ -0,0 +1,11 @@
+package stdlib
+
+import "github.com/d5/tengo/objects"
+
+func wrapError(err error) objects.Object {
+ if err == nil {
+ return objects.TrueValue
+ }
+
+ return &objects.Error{Value: &objects.String{Value: err.Error()}}
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/fmt.go b/vendor/github.com/d5/tengo/stdlib/fmt.go
new file mode 100644
index 00000000..9c75fc33
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/fmt.go
@@ -0,0 +1,116 @@
+package stdlib
+
+import (
+ "fmt"
+
+ "github.com/d5/tengo"
+ "github.com/d5/tengo/objects"
+)
+
+var fmtModule = map[string]objects.Object{
+ "print": &objects.UserFunction{Name: "print", Value: fmtPrint},
+ "printf": &objects.UserFunction{Name: "printf", Value: fmtPrintf},
+ "println": &objects.UserFunction{Name: "println", Value: fmtPrintln},
+ "sprintf": &objects.UserFunction{Name: "sprintf", Value: fmtSprintf},
+}
+
+func fmtPrint(args ...objects.Object) (ret objects.Object, err error) {
+ printArgs, err := getPrintArgs(args...)
+ if err != nil {
+ return nil, err
+ }
+
+ _, _ = fmt.Print(printArgs...)
+
+ return nil, nil
+}
+
+func fmtPrintf(args ...objects.Object) (ret objects.Object, err error) {
+ numArgs := len(args)
+ if numArgs == 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ format, ok := args[0].(*objects.String)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "format",
+ Expected: "string",
+ Found: args[0].TypeName(),
+ }
+ }
+ if numArgs == 1 {
+ fmt.Print(format)
+ return nil, nil
+ }
+
+ formatArgs := make([]interface{}, numArgs-1, numArgs-1)
+ for idx, arg := range args[1:] {
+ formatArgs[idx] = objects.ToInterface(arg)
+ }
+
+ fmt.Printf(format.Value, formatArgs...)
+
+ return nil, nil
+}
+
+func fmtPrintln(args ...objects.Object) (ret objects.Object, err error) {
+ printArgs, err := getPrintArgs(args...)
+ if err != nil {
+ return nil, err
+ }
+
+ printArgs = append(printArgs, "\n")
+ _, _ = fmt.Print(printArgs...)
+
+ return nil, nil
+}
+
+func fmtSprintf(args ...objects.Object) (ret objects.Object, err error) {
+ numArgs := len(args)
+ if numArgs == 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ format, ok := args[0].(*objects.String)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "format",
+ Expected: "string",
+ Found: args[0].TypeName(),
+ }
+ }
+ if numArgs == 1 {
+ return format, nil // okay to return 'format' directly as String is immutable
+ }
+
+ formatArgs := make([]interface{}, numArgs-1, numArgs-1)
+ for idx, arg := range args[1:] {
+ formatArgs[idx] = objects.ToInterface(arg)
+ }
+
+ s := fmt.Sprintf(format.Value, formatArgs...)
+
+ if len(s) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: s}, nil
+}
+
+func getPrintArgs(args ...objects.Object) ([]interface{}, error) {
+ var printArgs []interface{}
+ l := 0
+ for _, arg := range args {
+ s, _ := objects.ToString(arg)
+ slen := len(s)
+ if l+slen > tengo.MaxStringLen { // make sure length does not exceed the limit
+ return nil, objects.ErrStringLimit
+ }
+ l += slen
+
+ printArgs = append(printArgs, s)
+ }
+
+ return printArgs, nil
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/func_typedefs.go b/vendor/github.com/d5/tengo/stdlib/func_typedefs.go
new file mode 100644
index 00000000..26c7ddd9
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/func_typedefs.go
@@ -0,0 +1,1125 @@
+package stdlib
+
+import (
+ "fmt"
+
+ "github.com/d5/tengo"
+ "github.com/d5/tengo/objects"
+)
+
+// FuncAR transform a function of 'func()' signature
+// into CallableFunc type.
+func FuncAR(fn func()) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ fn()
+
+ return objects.UndefinedValue, nil
+ }
+}
+
+// FuncARI transform a function of 'func() int' signature
+// into CallableFunc type.
+func FuncARI(fn func() int) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ return &objects.Int{Value: int64(fn())}, nil
+ }
+}
+
+// FuncARI64 transform a function of 'func() int64' signature
+// into CallableFunc type.
+func FuncARI64(fn func() int64) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ return &objects.Int{Value: fn()}, nil
+ }
+}
+
+// FuncAI64RI64 transform a function of 'func(int64) int64' signature
+// into CallableFunc type.
+func FuncAI64RI64(fn func(int64) int64) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ return &objects.Int{Value: fn(i1)}, nil
+ }
+}
+
+// FuncAI64R transform a function of 'func(int64)' signature
+// into CallableFunc type.
+func FuncAI64R(fn func(int64)) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ fn(i1)
+
+ return objects.UndefinedValue, nil
+ }
+}
+
+// FuncARB transform a function of 'func() bool' signature
+// into CallableFunc type.
+func FuncARB(fn func() bool) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ if fn() {
+ return objects.TrueValue, nil
+ }
+
+ return objects.FalseValue, nil
+ }
+}
+
+// FuncARE transform a function of 'func() error' signature
+// into CallableFunc type.
+func FuncARE(fn func() error) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ return wrapError(fn()), nil
+ }
+}
+
+// FuncARS transform a function of 'func() string' signature
+// into CallableFunc type.
+func FuncARS(fn func() string) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s := fn()
+
+ if len(s) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: s}, nil
+ }
+}
+
+// FuncARSE transform a function of 'func() (string, error)' signature
+// into CallableFunc type.
+func FuncARSE(fn func() (string, error)) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ res, err := fn()
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ if len(res) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: res}, nil
+ }
+}
+
+// FuncARYE transform a function of 'func() ([]byte, error)' signature
+// into CallableFunc type.
+func FuncARYE(fn func() ([]byte, error)) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ res, err := fn()
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ if len(res) > tengo.MaxBytesLen {
+ return nil, objects.ErrBytesLimit
+ }
+
+ return &objects.Bytes{Value: res}, nil
+ }
+}
+
+// FuncARF transform a function of 'func() float64' signature
+// into CallableFunc type.
+func FuncARF(fn func() float64) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ return &objects.Float{Value: fn()}, nil
+ }
+}
+
+// FuncARSs transform a function of 'func() []string' signature
+// into CallableFunc type.
+func FuncARSs(fn func() []string) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ arr := &objects.Array{}
+ for _, elem := range fn() {
+ if len(elem) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ arr.Value = append(arr.Value, &objects.String{Value: elem})
+ }
+
+ return arr, nil
+ }
+}
+
+// FuncARIsE transform a function of 'func() ([]int, error)' signature
+// into CallableFunc type.
+func FuncARIsE(fn func() ([]int, error)) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ res, err := fn()
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ arr := &objects.Array{}
+ for _, v := range res {
+ arr.Value = append(arr.Value, &objects.Int{Value: int64(v)})
+ }
+
+ return arr, nil
+ }
+}
+
+// FuncAIRIs transform a function of 'func(int) []int' signature
+// into CallableFunc type.
+func FuncAIRIs(fn func(int) []int) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res := fn(i1)
+
+ arr := &objects.Array{}
+ for _, v := range res {
+ arr.Value = append(arr.Value, &objects.Int{Value: int64(v)})
+ }
+
+ return arr, nil
+ }
+}
+
+// FuncAFRF transform a function of 'func(float64) float64' signature
+// into CallableFunc type.
+func FuncAFRF(fn func(float64) float64) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ f1, ok := objects.ToFloat64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "float(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ return &objects.Float{Value: fn(f1)}, nil
+ }
+}
+
+// FuncAIR transform a function of 'func(int)' signature
+// into CallableFunc type.
+func FuncAIR(fn func(int)) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ fn(i1)
+
+ return objects.UndefinedValue, nil
+ }
+}
+
+// FuncAIRF transform a function of 'func(int) float64' signature
+// into CallableFunc type.
+func FuncAIRF(fn func(int) float64) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ return &objects.Float{Value: fn(i1)}, nil
+ }
+}
+
+// FuncAFRI transform a function of 'func(float64) int' signature
+// into CallableFunc type.
+func FuncAFRI(fn func(float64) int) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ f1, ok := objects.ToFloat64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "float(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ return &objects.Int{Value: int64(fn(f1))}, nil
+ }
+}
+
+// FuncAFFRF transform a function of 'func(float64, float64) float64' signature
+// into CallableFunc type.
+func FuncAFFRF(fn func(float64, float64) float64) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ f1, ok := objects.ToFloat64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "float(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ f2, ok := objects.ToFloat64(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "float(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ return &objects.Float{Value: fn(f1, f2)}, nil
+ }
+}
+
+// FuncAIFRF transform a function of 'func(int, float64) float64' signature
+// into CallableFunc type.
+func FuncAIFRF(fn func(int, float64) float64) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ f2, ok := objects.ToFloat64(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "float(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ return &objects.Float{Value: fn(i1, f2)}, nil
+ }
+}
+
+// FuncAFIRF transform a function of 'func(float64, int) float64' signature
+// into CallableFunc type.
+func FuncAFIRF(fn func(float64, int) float64) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ f1, ok := objects.ToFloat64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "float(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ return &objects.Float{Value: fn(f1, i2)}, nil
+ }
+}
+
+// FuncAFIRB transform a function of 'func(float64, int) bool' signature
+// into CallableFunc type.
+func FuncAFIRB(fn func(float64, int) bool) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ f1, ok := objects.ToFloat64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "float(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ if fn(f1, i2) {
+ return objects.TrueValue, nil
+ }
+
+ return objects.FalseValue, nil
+ }
+}
+
+// FuncAFRB transform a function of 'func(float64) bool' signature
+// into CallableFunc type.
+func FuncAFRB(fn func(float64) bool) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ f1, ok := objects.ToFloat64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "float(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ if fn(f1) {
+ return objects.TrueValue, nil
+ }
+
+ return objects.FalseValue, nil
+ }
+}
+
+// FuncASRS transform a function of 'func(string) string' signature into CallableFunc type.
+// User function will return 'true' if underlying native function returns nil.
+func FuncASRS(fn func(string) string) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s := fn(s1)
+
+ if len(s) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: s}, nil
+ }
+}
+
+// FuncASRSs transform a function of 'func(string) []string' signature into CallableFunc type.
+func FuncASRSs(fn func(string) []string) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res := fn(s1)
+
+ arr := &objects.Array{}
+ for _, elem := range res {
+ if len(elem) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ arr.Value = append(arr.Value, &objects.String{Value: elem})
+ }
+
+ return arr, nil
+ }
+}
+
+// FuncASRSE transform a function of 'func(string) (string, error)' signature into CallableFunc type.
+// User function will return 'true' if underlying native function returns nil.
+func FuncASRSE(fn func(string) (string, error)) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, err := fn(s1)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ if len(res) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: res}, nil
+ }
+}
+
+// FuncASRE transform a function of 'func(string) error' signature into CallableFunc type.
+// User function will return 'true' if underlying native function returns nil.
+func FuncASRE(fn func(string) error) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ return wrapError(fn(s1)), nil
+ }
+}
+
+// FuncASSRE transform a function of 'func(string, string) error' signature into CallableFunc type.
+// User function will return 'true' if underlying native function returns nil.
+func FuncASSRE(fn func(string, string) error) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ return wrapError(fn(s1, s2)), nil
+ }
+}
+
+// FuncASSRSs transform a function of 'func(string, string) []string' signature into CallableFunc type.
+func FuncASSRSs(fn func(string, string) []string) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ arr := &objects.Array{}
+ for _, res := range fn(s1, s2) {
+ if len(res) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ arr.Value = append(arr.Value, &objects.String{Value: res})
+ }
+
+ return arr, nil
+ }
+}
+
+// FuncASSIRSs transform a function of 'func(string, string, int) []string' signature into CallableFunc type.
+func FuncASSIRSs(fn func(string, string, int) []string) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 3 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ i3, ok := objects.ToInt(args[2])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ }
+
+ arr := &objects.Array{}
+ for _, res := range fn(s1, s2, i3) {
+ if len(res) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ arr.Value = append(arr.Value, &objects.String{Value: res})
+ }
+
+ return arr, nil
+ }
+}
+
+// FuncASSRI transform a function of 'func(string, string) int' signature into CallableFunc type.
+func FuncASSRI(fn func(string, string) int) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ return &objects.Int{Value: int64(fn(s1, s2))}, nil
+ }
+}
+
+// FuncASSRS transform a function of 'func(string, string) string' signature into CallableFunc type.
+func FuncASSRS(fn func(string, string) string) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ s := fn(s1, s2)
+
+ if len(s) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: s}, nil
+ }
+}
+
+// FuncASSRB transform a function of 'func(string, string) bool' signature into CallableFunc type.
+func FuncASSRB(fn func(string, string) bool) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ if fn(s1, s2) {
+ return objects.TrueValue, nil
+ }
+
+ return objects.FalseValue, nil
+ }
+}
+
+// FuncASsSRS transform a function of 'func([]string, string) string' signature into CallableFunc type.
+func FuncASsSRS(fn func([]string, string) string) objects.CallableFunc {
+ return func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ var ss1 []string
+ switch arg0 := args[0].(type) {
+ case *objects.Array:
+ for idx, a := range arg0.Value {
+ as, ok := objects.ToString(a)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: fmt.Sprintf("first[%d]", idx),
+ Expected: "string(compatible)",
+ Found: a.TypeName(),
+ }
+ }
+ ss1 = append(ss1, as)
+ }
+ case *objects.ImmutableArray:
+ for idx, a := range arg0.Value {
+ as, ok := objects.ToString(a)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: fmt.Sprintf("first[%d]", idx),
+ Expected: "string(compatible)",
+ Found: a.TypeName(),
+ }
+ }
+ ss1 = append(ss1, as)
+ }
+ default:
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "array",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ s := fn(ss1, s2)
+ if len(s) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: s}, nil
+ }
+}
+
+// FuncASI64RE transform a function of 'func(string, int64) error' signature
+// into CallableFunc type.
+func FuncASI64RE(fn func(string, int64) error) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := objects.ToInt64(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ return wrapError(fn(s1, i2)), nil
+ }
+}
+
+// FuncAIIRE transform a function of 'func(int, int) error' signature
+// into CallableFunc type.
+func FuncAIIRE(fn func(int, int) error) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ return wrapError(fn(i1, i2)), nil
+ }
+}
+
+// FuncASIRS transform a function of 'func(string, int) string' signature
+// into CallableFunc type.
+func FuncASIRS(fn func(string, int) string) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ s := fn(s1, i2)
+
+ if len(s) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: s}, nil
+ }
+}
+
+// FuncASIIRE transform a function of 'func(string, int, int) error' signature
+// into CallableFunc type.
+func FuncASIIRE(fn func(string, int, int) error) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 3 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ i3, ok := objects.ToInt(args[2])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ }
+
+ return wrapError(fn(s1, i2, i3)), nil
+ }
+}
+
+// FuncAYRIE transform a function of 'func([]byte) (int, error)' signature
+// into CallableFunc type.
+func FuncAYRIE(fn func([]byte) (int, error)) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ y1, ok := objects.ToByteSlice(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "bytes(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, err := fn(y1)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return &objects.Int{Value: int64(res)}, nil
+ }
+}
+
+// FuncASRIE transform a function of 'func(string) (int, error)' signature
+// into CallableFunc type.
+func FuncASRIE(fn func(string) (int, error)) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, err := fn(s1)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return &objects.Int{Value: int64(res)}, nil
+ }
+}
+
+// FuncAIRSsE transform a function of 'func(int) ([]string, error)' signature
+// into CallableFunc type.
+func FuncAIRSsE(fn func(int) ([]string, error)) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, err := fn(i1)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ arr := &objects.Array{}
+ for _, r := range res {
+ if len(r) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ arr.Value = append(arr.Value, &objects.String{Value: r})
+ }
+
+ return arr, nil
+ }
+}
+
+// FuncAIRS transform a function of 'func(int) string' signature
+// into CallableFunc type.
+func FuncAIRS(fn func(int) string) objects.CallableFunc {
+ return func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s := fn(i1)
+
+ if len(s) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: s}, nil
+ }
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/gensrcmods.go b/vendor/github.com/d5/tengo/stdlib/gensrcmods.go
new file mode 100644
index 00000000..fada66bd
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/gensrcmods.go
@@ -0,0 +1,53 @@
+// +build ignore
+
+package main
+
+import (
+ "bytes"
+ "io/ioutil"
+ "log"
+ "regexp"
+ "strconv"
+)
+
+var tengoModFileRE = regexp.MustCompile(`^srcmod_(\w+).tengo$`)
+
+func main() {
+ modules := make(map[string]string)
+
+ // enumerate all Tengo module files
+ files, err := ioutil.ReadDir(".")
+ if err != nil {
+ log.Fatal(err)
+ }
+ for _, file := range files {
+ m := tengoModFileRE.FindStringSubmatch(file.Name())
+ if m != nil {
+ modName := m[1]
+
+ src, err := ioutil.ReadFile(file.Name())
+ if err != nil {
+ log.Fatalf("file '%s' read error: %s", file.Name(), err.Error())
+ }
+
+ modules[modName] = string(src)
+ }
+ }
+
+ var out bytes.Buffer
+ out.WriteString(`// Code generated using gensrcmods.go; DO NOT EDIT.
+
+package stdlib
+
+// SourceModules are source type standard library modules.
+var SourceModules = map[string]string{` + "\n")
+ for modName, modSrc := range modules {
+ out.WriteString("\t\"" + modName + "\": " + strconv.Quote(modSrc) + ",\n")
+ }
+ out.WriteString("}\n")
+
+ const target = "source_modules.go"
+ if err := ioutil.WriteFile(target, out.Bytes(), 0644); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/json.go b/vendor/github.com/d5/tengo/stdlib/json.go
new file mode 100644
index 00000000..f913dc48
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/json.go
@@ -0,0 +1,126 @@
+package stdlib
+
+import (
+ "bytes"
+ gojson "encoding/json"
+
+ "github.com/d5/tengo/objects"
+ "github.com/d5/tengo/stdlib/json"
+)
+
+var jsonModule = map[string]objects.Object{
+ "decode": &objects.UserFunction{Name: "decode", Value: jsonDecode},
+ "encode": &objects.UserFunction{Name: "encode", Value: jsonEncode},
+ "indent": &objects.UserFunction{Name: "encode", Value: jsonIndent},
+ "html_escape": &objects.UserFunction{Name: "html_escape", Value: jsonHTMLEscape},
+}
+
+func jsonDecode(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ switch o := args[0].(type) {
+ case *objects.Bytes:
+ v, err := json.Decode(o.Value)
+ if err != nil {
+ return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil
+ }
+ return v, nil
+ case *objects.String:
+ v, err := json.Decode([]byte(o.Value))
+ if err != nil {
+ return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil
+ }
+ return v, nil
+ default:
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "bytes/string",
+ Found: args[0].TypeName(),
+ }
+ }
+}
+
+func jsonEncode(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ b, err := json.Encode(args[0])
+ if err != nil {
+ return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil
+ }
+
+ return &objects.Bytes{Value: b}, nil
+}
+
+func jsonIndent(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 3 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ prefix, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "prefix",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ indent, ok := objects.ToString(args[2])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "indent",
+ Expected: "string(compatible)",
+ Found: args[2].TypeName(),
+ }
+ }
+
+ switch o := args[0].(type) {
+ case *objects.Bytes:
+ var dst bytes.Buffer
+ err := gojson.Indent(&dst, o.Value, prefix, indent)
+ if err != nil {
+ return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil
+ }
+ return &objects.Bytes{Value: dst.Bytes()}, nil
+ case *objects.String:
+ var dst bytes.Buffer
+ err := gojson.Indent(&dst, []byte(o.Value), prefix, indent)
+ if err != nil {
+ return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil
+ }
+ return &objects.Bytes{Value: dst.Bytes()}, nil
+ default:
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "bytes/string",
+ Found: args[0].TypeName(),
+ }
+ }
+}
+
+func jsonHTMLEscape(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ switch o := args[0].(type) {
+ case *objects.Bytes:
+ var dst bytes.Buffer
+ gojson.HTMLEscape(&dst, o.Value)
+ return &objects.Bytes{Value: dst.Bytes()}, nil
+ case *objects.String:
+ var dst bytes.Buffer
+ gojson.HTMLEscape(&dst, []byte(o.Value))
+ return &objects.Bytes{Value: dst.Bytes()}, nil
+ default:
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "bytes/string",
+ Found: args[0].TypeName(),
+ }
+ }
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/json/decode.go b/vendor/github.com/d5/tengo/stdlib/json/decode.go
new file mode 100644
index 00000000..5a3fe6c7
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/json/decode.go
@@ -0,0 +1,374 @@
+// A modified version of Go's JSON implementation.
+
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package json
+
+import (
+ "strconv"
+ "unicode"
+ "unicode/utf16"
+ "unicode/utf8"
+
+ "github.com/d5/tengo/objects"
+)
+
+// Decode parses the JSON-encoded data and returns the result object.
+func Decode(data []byte) (objects.Object, error) {
+ var d decodeState
+ err := checkValid(data, &d.scan)
+ if err != nil {
+ return nil, err
+ }
+
+ d.init(data)
+ d.scan.reset()
+ d.scanWhile(scanSkipSpace)
+
+ return d.value()
+}
+
+// decodeState represents the state while decoding a JSON value.
+type decodeState struct {
+ data []byte
+ off int // next read offset in data
+ opcode int // last read result
+ scan scanner
+}
+
+// readIndex returns the position of the last byte read.
+func (d *decodeState) readIndex() int {
+ return d.off - 1
+}
+
+const phasePanicMsg = "JSON decoder out of sync - data changing underfoot?"
+
+func (d *decodeState) init(data []byte) *decodeState {
+ d.data = data
+ d.off = 0
+ return d
+}
+
+// scanNext processes the byte at d.data[d.off].
+func (d *decodeState) scanNext() {
+ if d.off < len(d.data) {
+ d.opcode = d.scan.step(&d.scan, d.data[d.off])
+ d.off++
+ } else {
+ d.opcode = d.scan.eof()
+ d.off = len(d.data) + 1 // mark processed EOF with len+1
+ }
+}
+
+// scanWhile processes bytes in d.data[d.off:] until it
+// receives a scan code not equal to op.
+func (d *decodeState) scanWhile(op int) {
+ s, data, i := &d.scan, d.data, d.off
+ for i < len(data) {
+ newOp := s.step(s, data[i])
+ i++
+ if newOp != op {
+ d.opcode = newOp
+ d.off = i
+ return
+ }
+ }
+
+ d.off = len(data) + 1 // mark processed EOF with len+1
+ d.opcode = d.scan.eof()
+}
+
+func (d *decodeState) value() (objects.Object, error) {
+ switch d.opcode {
+ default:
+ panic(phasePanicMsg)
+
+ case scanBeginArray:
+ o, err := d.array()
+ if err != nil {
+ return nil, err
+ }
+
+ d.scanNext()
+
+ return o, nil
+
+ case scanBeginObject:
+ o, err := d.object()
+ if err != nil {
+ return nil, err
+ }
+
+ d.scanNext()
+
+ return o, nil
+
+ case scanBeginLiteral:
+ return d.literal()
+ }
+}
+
+func (d *decodeState) array() (objects.Object, error) {
+ var arr []objects.Object
+ for {
+ // Look ahead for ] - can only happen on first iteration.
+ d.scanWhile(scanSkipSpace)
+ if d.opcode == scanEndArray {
+ break
+ }
+
+ o, err := d.value()
+ if err != nil {
+ return nil, err
+ }
+ arr = append(arr, o)
+
+ // Next token must be , or ].
+ if d.opcode == scanSkipSpace {
+ d.scanWhile(scanSkipSpace)
+ }
+ if d.opcode == scanEndArray {
+ break
+ }
+ if d.opcode != scanArrayValue {
+ panic(phasePanicMsg)
+ }
+ }
+
+ return &objects.Array{Value: arr}, nil
+}
+
+func (d *decodeState) object() (objects.Object, error) {
+ m := make(map[string]objects.Object)
+ for {
+ // Read opening " of string key or closing }.
+ d.scanWhile(scanSkipSpace)
+ if d.opcode == scanEndObject {
+ // closing } - can only happen on first iteration.
+ break
+ }
+ if d.opcode != scanBeginLiteral {
+ panic(phasePanicMsg)
+ }
+
+ // Read string key.
+ start := d.readIndex()
+ d.scanWhile(scanContinue)
+ item := d.data[start:d.readIndex()]
+ key, ok := unquote(item)
+ if !ok {
+ panic(phasePanicMsg)
+ }
+
+ // Read : before value.
+ if d.opcode == scanSkipSpace {
+ d.scanWhile(scanSkipSpace)
+ }
+ if d.opcode != scanObjectKey {
+ panic(phasePanicMsg)
+ }
+ d.scanWhile(scanSkipSpace)
+
+ // Read value.
+ o, err := d.value()
+ if err != nil {
+ return nil, err
+ }
+
+ m[key] = o
+
+ // Next token must be , or }.
+ if d.opcode == scanSkipSpace {
+ d.scanWhile(scanSkipSpace)
+ }
+ if d.opcode == scanEndObject {
+ break
+ }
+ if d.opcode != scanObjectValue {
+ panic(phasePanicMsg)
+ }
+ }
+
+ return &objects.Map{Value: m}, nil
+}
+
+func (d *decodeState) literal() (objects.Object, error) {
+ // All bytes inside literal return scanContinue op code.
+ start := d.readIndex()
+ d.scanWhile(scanContinue)
+
+ item := d.data[start:d.readIndex()]
+
+ switch c := item[0]; c {
+ case 'n': // null
+ return objects.UndefinedValue, nil
+
+ case 't', 'f': // true, false
+ if c == 't' {
+ return objects.TrueValue, nil
+ }
+ return objects.FalseValue, nil
+
+ case '"': // string
+ s, ok := unquote(item)
+ if !ok {
+ panic(phasePanicMsg)
+ }
+ return &objects.String{Value: s}, nil
+
+ default: // number
+ if c != '-' && (c < '0' || c > '9') {
+ panic(phasePanicMsg)
+ }
+
+ n, _ := strconv.ParseFloat(string(item), 10)
+ return &objects.Float{Value: n}, nil
+ }
+}
+
+// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
+// or it returns -1.
+func getu4(s []byte) rune {
+ if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
+ return -1
+ }
+ var r rune
+ for _, c := range s[2:6] {
+ switch {
+ case '0' <= c && c <= '9':
+ c = c - '0'
+ case 'a' <= c && c <= 'f':
+ c = c - 'a' + 10
+ case 'A' <= c && c <= 'F':
+ c = c - 'A' + 10
+ default:
+ return -1
+ }
+ r = r*16 + rune(c)
+ }
+ return r
+}
+
+// unquote converts a quoted JSON string literal s into an actual string t.
+// The rules are different than for Go, so cannot use strconv.Unquote.
+func unquote(s []byte) (t string, ok bool) {
+ s, ok = unquoteBytes(s)
+ t = string(s)
+ return
+}
+
+func unquoteBytes(s []byte) (t []byte, ok bool) {
+ if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
+ return
+ }
+ s = s[1 : len(s)-1]
+
+ // Check for unusual characters. If there are none,
+ // then no unquoting is needed, so return a slice of the
+ // original bytes.
+ r := 0
+ for r < len(s) {
+ c := s[r]
+ if c == '\\' || c == '"' || c < ' ' {
+ break
+ }
+ if c < utf8.RuneSelf {
+ r++
+ continue
+ }
+ rr, size := utf8.DecodeRune(s[r:])
+ if rr == utf8.RuneError && size == 1 {
+ break
+ }
+ r += size
+ }
+ if r == len(s) {
+ return s, true
+ }
+
+ b := make([]byte, len(s)+2*utf8.UTFMax)
+ w := copy(b, s[0:r])
+ for r < len(s) {
+ // Out of room? Can only happen if s is full of
+ // malformed UTF-8 and we're replacing each
+ // byte with RuneError.
+ if w >= len(b)-2*utf8.UTFMax {
+ nb := make([]byte, (len(b)+utf8.UTFMax)*2)
+ copy(nb, b[0:w])
+ b = nb
+ }
+ switch c := s[r]; {
+ case c == '\\':
+ r++
+ if r >= len(s) {
+ return
+ }
+ switch s[r] {
+ default:
+ return
+ case '"', '\\', '/', '\'':
+ b[w] = s[r]
+ r++
+ w++
+ case 'b':
+ b[w] = '\b'
+ r++
+ w++
+ case 'f':
+ b[w] = '\f'
+ r++
+ w++
+ case 'n':
+ b[w] = '\n'
+ r++
+ w++
+ case 'r':
+ b[w] = '\r'
+ r++
+ w++
+ case 't':
+ b[w] = '\t'
+ r++
+ w++
+ case 'u':
+ r--
+ rr := getu4(s[r:])
+ if rr < 0 {
+ return
+ }
+ r += 6
+ if utf16.IsSurrogate(rr) {
+ rr1 := getu4(s[r:])
+ if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
+ // A valid pair; consume.
+ r += 6
+ w += utf8.EncodeRune(b[w:], dec)
+ break
+ }
+ // Invalid surrogate; fall back to replacement rune.
+ rr = unicode.ReplacementChar
+ }
+ w += utf8.EncodeRune(b[w:], rr)
+ }
+
+ // Quote, control characters are invalid.
+ case c == '"', c < ' ':
+ return
+
+ // ASCII
+ case c < utf8.RuneSelf:
+ b[w] = c
+ r++
+ w++
+
+ // Coerce to well-formed UTF-8.
+ default:
+ rr, size := utf8.DecodeRune(s[r:])
+ r += size
+ w += utf8.EncodeRune(b[w:], rr)
+ }
+ }
+ return b[0:w], true
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/json/encode.go b/vendor/github.com/d5/tengo/stdlib/json/encode.go
new file mode 100644
index 00000000..2b8b17eb
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/json/encode.go
@@ -0,0 +1,147 @@
+// A modified version of Go's JSON implementation.
+
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package json
+
+import (
+ "encoding/base64"
+ "errors"
+ "math"
+ "strconv"
+
+ "github.com/d5/tengo/objects"
+)
+
+// Encode returns the JSON encoding of the object.
+func Encode(o objects.Object) ([]byte, error) {
+ var b []byte
+
+ switch o := o.(type) {
+ case *objects.Array:
+ b = append(b, '[')
+ len1 := len(o.Value) - 1
+ for idx, elem := range o.Value {
+ eb, err := Encode(elem)
+ if err != nil {
+ return nil, err
+ }
+ b = append(b, eb...)
+ if idx < len1 {
+ b = append(b, ',')
+ }
+ }
+ b = append(b, ']')
+ case *objects.ImmutableArray:
+ b = append(b, '[')
+ len1 := len(o.Value) - 1
+ for idx, elem := range o.Value {
+ eb, err := Encode(elem)
+ if err != nil {
+ return nil, err
+ }
+ b = append(b, eb...)
+ if idx < len1 {
+ b = append(b, ',')
+ }
+ }
+ b = append(b, ']')
+ case *objects.Map:
+ b = append(b, '{')
+ len1 := len(o.Value) - 1
+ idx := 0
+ for key, value := range o.Value {
+ b = strconv.AppendQuote(b, key)
+ b = append(b, ':')
+ eb, err := Encode(value)
+ if err != nil {
+ return nil, err
+ }
+ b = append(b, eb...)
+ if idx < len1 {
+ b = append(b, ',')
+ }
+ idx++
+ }
+ b = append(b, '}')
+ case *objects.ImmutableMap:
+ b = append(b, '{')
+ len1 := len(o.Value) - 1
+ idx := 0
+ for key, value := range o.Value {
+ b = strconv.AppendQuote(b, key)
+ b = append(b, ':')
+ eb, err := Encode(value)
+ if err != nil {
+ return nil, err
+ }
+ b = append(b, eb...)
+ if idx < len1 {
+ b = append(b, ',')
+ }
+ idx++
+ }
+ b = append(b, '}')
+ case *objects.Bool:
+ if o.IsFalsy() {
+ b = strconv.AppendBool(b, false)
+ } else {
+ b = strconv.AppendBool(b, true)
+ }
+ case *objects.Bytes:
+ b = append(b, '"')
+ encodedLen := base64.StdEncoding.EncodedLen(len(o.Value))
+ dst := make([]byte, encodedLen)
+ base64.StdEncoding.Encode(dst, o.Value)
+ b = append(b, dst...)
+ b = append(b, '"')
+ case *objects.Char:
+ b = strconv.AppendInt(b, int64(o.Value), 10)
+ case *objects.Float:
+ var y []byte
+
+ f := o.Value
+ if math.IsInf(f, 0) || math.IsNaN(f) {
+ return nil, errors.New("unsupported float value")
+ }
+
+ // Convert as if by ES6 number to string conversion.
+ // This matches most other JSON generators.
+ abs := math.Abs(f)
+ fmt := byte('f')
+ if abs != 0 {
+ if abs < 1e-6 || abs >= 1e21 {
+ fmt = 'e'
+ }
+ }
+ y = strconv.AppendFloat(y, f, fmt, -1, 64)
+ if fmt == 'e' {
+ // clean up e-09 to e-9
+ n := len(y)
+ if n >= 4 && y[n-4] == 'e' && y[n-3] == '-' && y[n-2] == '0' {
+ y[n-2] = y[n-1]
+ y = y[:n-1]
+ }
+ }
+
+ b = append(b, y...)
+ case *objects.Int:
+ b = strconv.AppendInt(b, o.Value, 10)
+ case *objects.String:
+ b = strconv.AppendQuote(b, o.Value)
+ case *objects.Time:
+ y, err := o.Value.MarshalJSON()
+ if err != nil {
+ return nil, err
+ }
+ b = append(b, y...)
+ case *objects.Undefined:
+ b = append(b, "null"...)
+ default:
+ // unknown type: ignore
+ }
+
+ return b, nil
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/json/scanner.go b/vendor/github.com/d5/tengo/stdlib/json/scanner.go
new file mode 100644
index 00000000..8fc6776d
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/json/scanner.go
@@ -0,0 +1,559 @@
+// A modified version of Go's JSON implementation.
+
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package json
+
+import "strconv"
+
+func checkValid(data []byte, scan *scanner) error {
+ scan.reset()
+ for _, c := range data {
+ scan.bytes++
+ if scan.step(scan, c) == scanError {
+ return scan.err
+ }
+ }
+ if scan.eof() == scanError {
+ return scan.err
+ }
+ return nil
+}
+
+// A SyntaxError is a description of a JSON syntax error.
+type SyntaxError struct {
+ msg string // description of error
+ Offset int64 // error occurred after reading Offset bytes
+}
+
+func (e *SyntaxError) Error() string { return e.msg }
+
+// A scanner is a JSON scanning state machine.
+// Callers call scan.reset() and then pass bytes in one at a time
+// by calling scan.step(&scan, c) for each byte.
+// The return value, referred to as an opcode, tells the
+// caller about significant parsing events like beginning
+// and ending literals, objects, and arrays, so that the
+// caller can follow along if it wishes.
+// The return value scanEnd indicates that a single top-level
+// JSON value has been completed, *before* the byte that
+// just got passed in. (The indication must be delayed in order
+// to recognize the end of numbers: is 123 a whole value or
+// the beginning of 12345e+6?).
+type scanner struct {
+ // The step is a func to be called to execute the next transition.
+ // Also tried using an integer constant and a single func
+ // with a switch, but using the func directly was 10% faster
+ // on a 64-bit Mac Mini, and it's nicer to read.
+ step func(*scanner, byte) int
+
+ // Reached end of top-level value.
+ endTop bool
+
+ // Stack of what we're in the middle of - array values, object keys, object values.
+ parseState []int
+
+ // Error that happened, if any.
+ err error
+
+ // total bytes consumed, updated by decoder.Decode
+ bytes int64
+}
+
+// These values are returned by the state transition functions
+// assigned to scanner.state and the method scanner.eof.
+// They give details about the current state of the scan that
+// callers might be interested to know about.
+// It is okay to ignore the return value of any particular
+// call to scanner.state: if one call returns scanError,
+// every subsequent call will return scanError too.
+const (
+ // Continue.
+ scanContinue = iota // uninteresting byte
+ scanBeginLiteral // end implied by next result != scanContinue
+ scanBeginObject // begin object
+ scanObjectKey // just finished object key (string)
+ scanObjectValue // just finished non-last object value
+ scanEndObject // end object (implies scanObjectValue if possible)
+ scanBeginArray // begin array
+ scanArrayValue // just finished array value
+ scanEndArray // end array (implies scanArrayValue if possible)
+ scanSkipSpace // space byte; can skip; known to be last "continue" result
+
+ // Stop.
+ scanEnd // top-level value ended *before* this byte; known to be first "stop" result
+ scanError // hit an error, scanner.err.
+)
+
+// These values are stored in the parseState stack.
+// They give the current state of a composite value
+// being scanned. If the parser is inside a nested value
+// the parseState describes the nested state, outermost at entry 0.
+const (
+ parseObjectKey = iota // parsing object key (before colon)
+ parseObjectValue // parsing object value (after colon)
+ parseArrayValue // parsing array value
+)
+
+// reset prepares the scanner for use.
+// It must be called before calling s.step.
+func (s *scanner) reset() {
+ s.step = stateBeginValue
+ s.parseState = s.parseState[0:0]
+ s.err = nil
+ s.endTop = false
+}
+
+// eof tells the scanner that the end of input has been reached.
+// It returns a scan status just as s.step does.
+func (s *scanner) eof() int {
+ if s.err != nil {
+ return scanError
+ }
+ if s.endTop {
+ return scanEnd
+ }
+ s.step(s, ' ')
+ if s.endTop {
+ return scanEnd
+ }
+ if s.err == nil {
+ s.err = &SyntaxError{"unexpected end of JSON input", s.bytes}
+ }
+ return scanError
+}
+
+// pushParseState pushes a new parse state p onto the parse stack.
+func (s *scanner) pushParseState(p int) {
+ s.parseState = append(s.parseState, p)
+}
+
+// popParseState pops a parse state (already obtained) off the stack
+// and updates s.step accordingly.
+func (s *scanner) popParseState() {
+ n := len(s.parseState) - 1
+ s.parseState = s.parseState[0:n]
+ if n == 0 {
+ s.step = stateEndTop
+ s.endTop = true
+ } else {
+ s.step = stateEndValue
+ }
+}
+
+func isSpace(c byte) bool {
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n'
+}
+
+// stateBeginValueOrEmpty is the state after reading `[`.
+func stateBeginValueOrEmpty(s *scanner, c byte) int {
+ if c <= ' ' && isSpace(c) {
+ return scanSkipSpace
+ }
+ if c == ']' {
+ return stateEndValue(s, c)
+ }
+ return stateBeginValue(s, c)
+}
+
+// stateBeginValue is the state at the beginning of the input.
+func stateBeginValue(s *scanner, c byte) int {
+ if c <= ' ' && isSpace(c) {
+ return scanSkipSpace
+ }
+ switch c {
+ case '{':
+ s.step = stateBeginStringOrEmpty
+ s.pushParseState(parseObjectKey)
+ return scanBeginObject
+ case '[':
+ s.step = stateBeginValueOrEmpty
+ s.pushParseState(parseArrayValue)
+ return scanBeginArray
+ case '"':
+ s.step = stateInString
+ return scanBeginLiteral
+ case '-':
+ s.step = stateNeg
+ return scanBeginLiteral
+ case '0': // beginning of 0.123
+ s.step = state0
+ return scanBeginLiteral
+ case 't': // beginning of true
+ s.step = stateT
+ return scanBeginLiteral
+ case 'f': // beginning of false
+ s.step = stateF
+ return scanBeginLiteral
+ case 'n': // beginning of null
+ s.step = stateN
+ return scanBeginLiteral
+ }
+ if '1' <= c && c <= '9' { // beginning of 1234.5
+ s.step = state1
+ return scanBeginLiteral
+ }
+ return s.error(c, "looking for beginning of value")
+}
+
+// stateBeginStringOrEmpty is the state after reading `{`.
+func stateBeginStringOrEmpty(s *scanner, c byte) int {
+ if c <= ' ' && isSpace(c) {
+ return scanSkipSpace
+ }
+ if c == '}' {
+ n := len(s.parseState)
+ s.parseState[n-1] = parseObjectValue
+ return stateEndValue(s, c)
+ }
+ return stateBeginString(s, c)
+}
+
+// stateBeginString is the state after reading `{"key": value,`.
+func stateBeginString(s *scanner, c byte) int {
+ if c <= ' ' && isSpace(c) {
+ return scanSkipSpace
+ }
+ if c == '"' {
+ s.step = stateInString
+ return scanBeginLiteral
+ }
+ return s.error(c, "looking for beginning of object key string")
+}
+
+// stateEndValue is the state after completing a value,
+// such as after reading `{}` or `true` or `["x"`.
+func stateEndValue(s *scanner, c byte) int {
+ n := len(s.parseState)
+ if n == 0 {
+ // Completed top-level before the current byte.
+ s.step = stateEndTop
+ s.endTop = true
+ return stateEndTop(s, c)
+ }
+ if c <= ' ' && isSpace(c) {
+ s.step = stateEndValue
+ return scanSkipSpace
+ }
+ ps := s.parseState[n-1]
+ switch ps {
+ case parseObjectKey:
+ if c == ':' {
+ s.parseState[n-1] = parseObjectValue
+ s.step = stateBeginValue
+ return scanObjectKey
+ }
+ return s.error(c, "after object key")
+ case parseObjectValue:
+ if c == ',' {
+ s.parseState[n-1] = parseObjectKey
+ s.step = stateBeginString
+ return scanObjectValue
+ }
+ if c == '}' {
+ s.popParseState()
+ return scanEndObject
+ }
+ return s.error(c, "after object key:value pair")
+ case parseArrayValue:
+ if c == ',' {
+ s.step = stateBeginValue
+ return scanArrayValue
+ }
+ if c == ']' {
+ s.popParseState()
+ return scanEndArray
+ }
+ return s.error(c, "after array element")
+ }
+ return s.error(c, "")
+}
+
+// stateEndTop is the state after finishing the top-level value,
+// such as after reading `{}` or `[1,2,3]`.
+// Only space characters should be seen now.
+func stateEndTop(s *scanner, c byte) int {
+ if !isSpace(c) {
+ // Complain about non-space byte on next call.
+ s.error(c, "after top-level value")
+ }
+ return scanEnd
+}
+
+// stateInString is the state after reading `"`.
+func stateInString(s *scanner, c byte) int {
+ if c == '"' {
+ s.step = stateEndValue
+ return scanContinue
+ }
+ if c == '\\' {
+ s.step = stateInStringEsc
+ return scanContinue
+ }
+ if c < 0x20 {
+ return s.error(c, "in string literal")
+ }
+ return scanContinue
+}
+
+// stateInStringEsc is the state after reading `"\` during a quoted string.
+func stateInStringEsc(s *scanner, c byte) int {
+ switch c {
+ case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
+ s.step = stateInString
+ return scanContinue
+ case 'u':
+ s.step = stateInStringEscU
+ return scanContinue
+ }
+ return s.error(c, "in string escape code")
+}
+
+// stateInStringEscU is the state after reading `"\u` during a quoted string.
+func stateInStringEscU(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
+ s.step = stateInStringEscU1
+ return scanContinue
+ }
+ // numbers
+ return s.error(c, "in \\u hexadecimal character escape")
+}
+
+// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
+func stateInStringEscU1(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
+ s.step = stateInStringEscU12
+ return scanContinue
+ }
+ // numbers
+ return s.error(c, "in \\u hexadecimal character escape")
+}
+
+// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
+func stateInStringEscU12(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
+ s.step = stateInStringEscU123
+ return scanContinue
+ }
+ // numbers
+ return s.error(c, "in \\u hexadecimal character escape")
+}
+
+// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
+func stateInStringEscU123(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
+ s.step = stateInString
+ return scanContinue
+ }
+ // numbers
+ return s.error(c, "in \\u hexadecimal character escape")
+}
+
+// stateNeg is the state after reading `-` during a number.
+func stateNeg(s *scanner, c byte) int {
+ if c == '0' {
+ s.step = state0
+ return scanContinue
+ }
+ if '1' <= c && c <= '9' {
+ s.step = state1
+ return scanContinue
+ }
+ return s.error(c, "in numeric literal")
+}
+
+// state1 is the state after reading a non-zero integer during a number,
+// such as after reading `1` or `100` but not `0`.
+func state1(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' {
+ s.step = state1
+ return scanContinue
+ }
+ return state0(s, c)
+}
+
+// state0 is the state after reading `0` during a number.
+func state0(s *scanner, c byte) int {
+ if c == '.' {
+ s.step = stateDot
+ return scanContinue
+ }
+ if c == 'e' || c == 'E' {
+ s.step = stateE
+ return scanContinue
+ }
+ return stateEndValue(s, c)
+}
+
+// stateDot is the state after reading the integer and decimal point in a number,
+// such as after reading `1.`.
+func stateDot(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' {
+ s.step = stateDot0
+ return scanContinue
+ }
+ return s.error(c, "after decimal point in numeric literal")
+}
+
+// stateDot0 is the state after reading the integer, decimal point, and subsequent
+// digits of a number, such as after reading `3.14`.
+func stateDot0(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' {
+ return scanContinue
+ }
+ if c == 'e' || c == 'E' {
+ s.step = stateE
+ return scanContinue
+ }
+ return stateEndValue(s, c)
+}
+
+// stateE is the state after reading the mantissa and e in a number,
+// such as after reading `314e` or `0.314e`.
+func stateE(s *scanner, c byte) int {
+ if c == '+' || c == '-' {
+ s.step = stateESign
+ return scanContinue
+ }
+ return stateESign(s, c)
+}
+
+// stateESign is the state after reading the mantissa, e, and sign in a number,
+// such as after reading `314e-` or `0.314e+`.
+func stateESign(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' {
+ s.step = stateE0
+ return scanContinue
+ }
+ return s.error(c, "in exponent of numeric literal")
+}
+
+// stateE0 is the state after reading the mantissa, e, optional sign,
+// and at least one digit of the exponent in a number,
+// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
+func stateE0(s *scanner, c byte) int {
+ if '0' <= c && c <= '9' {
+ return scanContinue
+ }
+ return stateEndValue(s, c)
+}
+
+// stateT is the state after reading `t`.
+func stateT(s *scanner, c byte) int {
+ if c == 'r' {
+ s.step = stateTr
+ return scanContinue
+ }
+ return s.error(c, "in literal true (expecting 'r')")
+}
+
+// stateTr is the state after reading `tr`.
+func stateTr(s *scanner, c byte) int {
+ if c == 'u' {
+ s.step = stateTru
+ return scanContinue
+ }
+ return s.error(c, "in literal true (expecting 'u')")
+}
+
+// stateTru is the state after reading `tru`.
+func stateTru(s *scanner, c byte) int {
+ if c == 'e' {
+ s.step = stateEndValue
+ return scanContinue
+ }
+ return s.error(c, "in literal true (expecting 'e')")
+}
+
+// stateF is the state after reading `f`.
+func stateF(s *scanner, c byte) int {
+ if c == 'a' {
+ s.step = stateFa
+ return scanContinue
+ }
+ return s.error(c, "in literal false (expecting 'a')")
+}
+
+// stateFa is the state after reading `fa`.
+func stateFa(s *scanner, c byte) int {
+ if c == 'l' {
+ s.step = stateFal
+ return scanContinue
+ }
+ return s.error(c, "in literal false (expecting 'l')")
+}
+
+// stateFal is the state after reading `fal`.
+func stateFal(s *scanner, c byte) int {
+ if c == 's' {
+ s.step = stateFals
+ return scanContinue
+ }
+ return s.error(c, "in literal false (expecting 's')")
+}
+
+// stateFals is the state after reading `fals`.
+func stateFals(s *scanner, c byte) int {
+ if c == 'e' {
+ s.step = stateEndValue
+ return scanContinue
+ }
+ return s.error(c, "in literal false (expecting 'e')")
+}
+
+// stateN is the state after reading `n`.
+func stateN(s *scanner, c byte) int {
+ if c == 'u' {
+ s.step = stateNu
+ return scanContinue
+ }
+ return s.error(c, "in literal null (expecting 'u')")
+}
+
+// stateNu is the state after reading `nu`.
+func stateNu(s *scanner, c byte) int {
+ if c == 'l' {
+ s.step = stateNul
+ return scanContinue
+ }
+ return s.error(c, "in literal null (expecting 'l')")
+}
+
+// stateNul is the state after reading `nul`.
+func stateNul(s *scanner, c byte) int {
+ if c == 'l' {
+ s.step = stateEndValue
+ return scanContinue
+ }
+ return s.error(c, "in literal null (expecting 'l')")
+}
+
+// stateError is the state after reaching a syntax error,
+// such as after reading `[1}` or `5.1.2`.
+func stateError(s *scanner, c byte) int {
+ return scanError
+}
+
+// error records an error and switches to the error state.
+func (s *scanner) error(c byte, context string) int {
+ s.step = stateError
+ s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes}
+ return scanError
+}
+
+// quoteChar formats c as a quoted character literal
+func quoteChar(c byte) string {
+ // special cases - different from quoted strings
+ if c == '\'' {
+ return `'\''`
+ }
+ if c == '"' {
+ return `'"'`
+ }
+
+ // use quoted string with different quotation marks
+ s := strconv.Quote(string(c))
+ return "'" + s[1:len(s)-1] + "'"
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/math.go b/vendor/github.com/d5/tengo/stdlib/math.go
new file mode 100644
index 00000000..08d82bdf
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/math.go
@@ -0,0 +1,74 @@
+package stdlib
+
+import (
+ "math"
+
+ "github.com/d5/tengo/objects"
+)
+
+var mathModule = map[string]objects.Object{
+ "e": &objects.Float{Value: math.E},
+ "pi": &objects.Float{Value: math.Pi},
+ "phi": &objects.Float{Value: math.Phi},
+ "sqrt2": &objects.Float{Value: math.Sqrt2},
+ "sqrtE": &objects.Float{Value: math.SqrtE},
+ "sqrtPi": &objects.Float{Value: math.SqrtPi},
+ "sqrtPhi": &objects.Float{Value: math.SqrtPhi},
+ "ln2": &objects.Float{Value: math.Ln2},
+ "log2E": &objects.Float{Value: math.Log2E},
+ "ln10": &objects.Float{Value: math.Ln10},
+ "log10E": &objects.Float{Value: math.Log10E},
+ "abs": &objects.UserFunction{Name: "abs", Value: FuncAFRF(math.Abs)},
+ "acos": &objects.UserFunction{Name: "acos", Value: FuncAFRF(math.Acos)},
+ "acosh": &objects.UserFunction{Name: "acosh", Value: FuncAFRF(math.Acosh)},
+ "asin": &objects.UserFunction{Name: "asin", Value: FuncAFRF(math.Asin)},
+ "asinh": &objects.UserFunction{Name: "asinh", Value: FuncAFRF(math.Asinh)},
+ "atan": &objects.UserFunction{Name: "atan", Value: FuncAFRF(math.Atan)},
+ "atan2": &objects.UserFunction{Name: "atan2", Value: FuncAFFRF(math.Atan2)},
+ "atanh": &objects.UserFunction{Name: "atanh", Value: FuncAFRF(math.Atanh)},
+ "cbrt": &objects.UserFunction{Name: "cbrt", Value: FuncAFRF(math.Cbrt)},
+ "ceil": &objects.UserFunction{Name: "ceil", Value: FuncAFRF(math.Ceil)},
+ "copysign": &objects.UserFunction{Name: "copysign", Value: FuncAFFRF(math.Copysign)},
+ "cos": &objects.UserFunction{Name: "cos", Value: FuncAFRF(math.Cos)},
+ "cosh": &objects.UserFunction{Name: "cosh", Value: FuncAFRF(math.Cosh)},
+ "dim": &objects.UserFunction{Name: "dim", Value: FuncAFFRF(math.Dim)},
+ "erf": &objects.UserFunction{Name: "erf", Value: FuncAFRF(math.Erf)},
+ "erfc": &objects.UserFunction{Name: "erfc", Value: FuncAFRF(math.Erfc)},
+ "exp": &objects.UserFunction{Name: "exp", Value: FuncAFRF(math.Exp)},
+ "exp2": &objects.UserFunction{Name: "exp2", Value: FuncAFRF(math.Exp2)},
+ "expm1": &objects.UserFunction{Name: "expm1", Value: FuncAFRF(math.Expm1)},
+ "floor": &objects.UserFunction{Name: "floor", Value: FuncAFRF(math.Floor)},
+ "gamma": &objects.UserFunction{Name: "gamma", Value: FuncAFRF(math.Gamma)},
+ "hypot": &objects.UserFunction{Name: "hypot", Value: FuncAFFRF(math.Hypot)},
+ "ilogb": &objects.UserFunction{Name: "ilogb", Value: FuncAFRI(math.Ilogb)},
+ "inf": &objects.UserFunction{Name: "inf", Value: FuncAIRF(math.Inf)},
+ "is_inf": &objects.UserFunction{Name: "is_inf", Value: FuncAFIRB(math.IsInf)},
+ "is_nan": &objects.UserFunction{Name: "is_nan", Value: FuncAFRB(math.IsNaN)},
+ "j0": &objects.UserFunction{Name: "j0", Value: FuncAFRF(math.J0)},
+ "j1": &objects.UserFunction{Name: "j1", Value: FuncAFRF(math.J1)},
+ "jn": &objects.UserFunction{Name: "jn", Value: FuncAIFRF(math.Jn)},
+ "ldexp": &objects.UserFunction{Name: "ldexp", Value: FuncAFIRF(math.Ldexp)},
+ "log": &objects.UserFunction{Name: "log", Value: FuncAFRF(math.Log)},
+ "log10": &objects.UserFunction{Name: "log10", Value: FuncAFRF(math.Log10)},
+ "log1p": &objects.UserFunction{Name: "log1p", Value: FuncAFRF(math.Log1p)},
+ "log2": &objects.UserFunction{Name: "log2", Value: FuncAFRF(math.Log2)},
+ "logb": &objects.UserFunction{Name: "logb", Value: FuncAFRF(math.Logb)},
+ "max": &objects.UserFunction{Name: "max", Value: FuncAFFRF(math.Max)},
+ "min": &objects.UserFunction{Name: "min", Value: FuncAFFRF(math.Min)},
+ "mod": &objects.UserFunction{Name: "mod", Value: FuncAFFRF(math.Mod)},
+ "nan": &objects.UserFunction{Name: "nan", Value: FuncARF(math.NaN)},
+ "nextafter": &objects.UserFunction{Name: "nextafter", Value: FuncAFFRF(math.Nextafter)},
+ "pow": &objects.UserFunction{Name: "pow", Value: FuncAFFRF(math.Pow)},
+ "pow10": &objects.UserFunction{Name: "pow10", Value: FuncAIRF(math.Pow10)},
+ "remainder": &objects.UserFunction{Name: "remainder", Value: FuncAFFRF(math.Remainder)},
+ "signbit": &objects.UserFunction{Name: "signbit", Value: FuncAFRB(math.Signbit)},
+ "sin": &objects.UserFunction{Name: "sin", Value: FuncAFRF(math.Sin)},
+ "sinh": &objects.UserFunction{Name: "sinh", Value: FuncAFRF(math.Sinh)},
+ "sqrt": &objects.UserFunction{Name: "sqrt", Value: FuncAFRF(math.Sqrt)},
+ "tan": &objects.UserFunction{Name: "tan", Value: FuncAFRF(math.Tan)},
+ "tanh": &objects.UserFunction{Name: "tanh", Value: FuncAFRF(math.Tanh)},
+ "trunc": &objects.UserFunction{Name: "trunc", Value: FuncAFRF(math.Trunc)},
+ "y0": &objects.UserFunction{Name: "y0", Value: FuncAFRF(math.Y0)},
+ "y1": &objects.UserFunction{Name: "y1", Value: FuncAFRF(math.Y1)},
+ "yn": &objects.UserFunction{Name: "yn", Value: FuncAIFRF(math.Yn)},
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/os.go b/vendor/github.com/d5/tengo/stdlib/os.go
new file mode 100644
index 00000000..a7890cc1
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/os.go
@@ -0,0 +1,492 @@
+package stdlib
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+
+ "github.com/d5/tengo"
+ "github.com/d5/tengo/objects"
+)
+
+var osModule = map[string]objects.Object{
+ "o_rdonly": &objects.Int{Value: int64(os.O_RDONLY)},
+ "o_wronly": &objects.Int{Value: int64(os.O_WRONLY)},
+ "o_rdwr": &objects.Int{Value: int64(os.O_RDWR)},
+ "o_append": &objects.Int{Value: int64(os.O_APPEND)},
+ "o_create": &objects.Int{Value: int64(os.O_CREATE)},
+ "o_excl": &objects.Int{Value: int64(os.O_EXCL)},
+ "o_sync": &objects.Int{Value: int64(os.O_SYNC)},
+ "o_trunc": &objects.Int{Value: int64(os.O_TRUNC)},
+ "mode_dir": &objects.Int{Value: int64(os.ModeDir)},
+ "mode_append": &objects.Int{Value: int64(os.ModeAppend)},
+ "mode_exclusive": &objects.Int{Value: int64(os.ModeExclusive)},
+ "mode_temporary": &objects.Int{Value: int64(os.ModeTemporary)},
+ "mode_symlink": &objects.Int{Value: int64(os.ModeSymlink)},
+ "mode_device": &objects.Int{Value: int64(os.ModeDevice)},
+ "mode_named_pipe": &objects.Int{Value: int64(os.ModeNamedPipe)},
+ "mode_socket": &objects.Int{Value: int64(os.ModeSocket)},
+ "mode_setuid": &objects.Int{Value: int64(os.ModeSetuid)},
+ "mode_setgui": &objects.Int{Value: int64(os.ModeSetgid)},
+ "mode_char_device": &objects.Int{Value: int64(os.ModeCharDevice)},
+ "mode_sticky": &objects.Int{Value: int64(os.ModeSticky)},
+ "mode_type": &objects.Int{Value: int64(os.ModeType)},
+ "mode_perm": &objects.Int{Value: int64(os.ModePerm)},
+ "path_separator": &objects.Char{Value: os.PathSeparator},
+ "path_list_separator": &objects.Char{Value: os.PathListSeparator},
+ "dev_null": &objects.String{Value: os.DevNull},
+ "seek_set": &objects.Int{Value: int64(io.SeekStart)},
+ "seek_cur": &objects.Int{Value: int64(io.SeekCurrent)},
+ "seek_end": &objects.Int{Value: int64(io.SeekEnd)},
+ "args": &objects.UserFunction{Name: "args", Value: osArgs}, // args() => array(string)
+ "chdir": &objects.UserFunction{Name: "chdir", Value: FuncASRE(os.Chdir)}, // chdir(dir string) => error
+ "chmod": osFuncASFmRE("chmod", os.Chmod), // chmod(name string, mode int) => error
+ "chown": &objects.UserFunction{Name: "chown", Value: FuncASIIRE(os.Chown)}, // chown(name string, uid int, gid int) => error
+ "clearenv": &objects.UserFunction{Name: "clearenv", Value: FuncAR(os.Clearenv)}, // clearenv()
+ "environ": &objects.UserFunction{Name: "environ", Value: FuncARSs(os.Environ)}, // environ() => array(string)
+ "exit": &objects.UserFunction{Name: "exit", Value: FuncAIR(os.Exit)}, // exit(code int)
+ "expand_env": &objects.UserFunction{Name: "expand_env", Value: osExpandEnv}, // expand_env(s string) => string
+ "getegid": &objects.UserFunction{Name: "getegid", Value: FuncARI(os.Getegid)}, // getegid() => int
+ "getenv": &objects.UserFunction{Name: "getenv", Value: FuncASRS(os.Getenv)}, // getenv(s string) => string
+ "geteuid": &objects.UserFunction{Name: "geteuid", Value: FuncARI(os.Geteuid)}, // geteuid() => int
+ "getgid": &objects.UserFunction{Name: "getgid", Value: FuncARI(os.Getgid)}, // getgid() => int
+ "getgroups": &objects.UserFunction{Name: "getgroups", Value: FuncARIsE(os.Getgroups)}, // getgroups() => array(string)/error
+ "getpagesize": &objects.UserFunction{Name: "getpagesize", Value: FuncARI(os.Getpagesize)}, // getpagesize() => int
+ "getpid": &objects.UserFunction{Name: "getpid", Value: FuncARI(os.Getpid)}, // getpid() => int
+ "getppid": &objects.UserFunction{Name: "getppid", Value: FuncARI(os.Getppid)}, // getppid() => int
+ "getuid": &objects.UserFunction{Name: "getuid", Value: FuncARI(os.Getuid)}, // getuid() => int
+ "getwd": &objects.UserFunction{Name: "getwd", Value: FuncARSE(os.Getwd)}, // getwd() => string/error
+ "hostname": &objects.UserFunction{Name: "hostname", Value: FuncARSE(os.Hostname)}, // hostname() => string/error
+ "lchown": &objects.UserFunction{Name: "lchown", Value: FuncASIIRE(os.Lchown)}, // lchown(name string, uid int, gid int) => error
+ "link": &objects.UserFunction{Name: "link", Value: FuncASSRE(os.Link)}, // link(oldname string, newname string) => error
+ "lookup_env": &objects.UserFunction{Name: "lookup_env", Value: osLookupEnv}, // lookup_env(key string) => string/false
+ "mkdir": osFuncASFmRE("mkdir", os.Mkdir), // mkdir(name string, perm int) => error
+ "mkdir_all": osFuncASFmRE("mkdir_all", os.MkdirAll), // mkdir_all(name string, perm int) => error
+ "readlink": &objects.UserFunction{Name: "readlink", Value: FuncASRSE(os.Readlink)}, // readlink(name string) => string/error
+ "remove": &objects.UserFunction{Name: "remove", Value: FuncASRE(os.Remove)}, // remove(name string) => error
+ "remove_all": &objects.UserFunction{Name: "remove_all", Value: FuncASRE(os.RemoveAll)}, // remove_all(name string) => error
+ "rename": &objects.UserFunction{Name: "rename", Value: FuncASSRE(os.Rename)}, // rename(oldpath string, newpath string) => error
+ "setenv": &objects.UserFunction{Name: "setenv", Value: FuncASSRE(os.Setenv)}, // setenv(key string, value string) => error
+ "symlink": &objects.UserFunction{Name: "symlink", Value: FuncASSRE(os.Symlink)}, // symlink(oldname string newname string) => error
+ "temp_dir": &objects.UserFunction{Name: "temp_dir", Value: FuncARS(os.TempDir)}, // temp_dir() => string
+ "truncate": &objects.UserFunction{Name: "truncate", Value: FuncASI64RE(os.Truncate)}, // truncate(name string, size int) => error
+ "unsetenv": &objects.UserFunction{Name: "unsetenv", Value: FuncASRE(os.Unsetenv)}, // unsetenv(key string) => error
+ "create": &objects.UserFunction{Name: "create", Value: osCreate}, // create(name string) => imap(file)/error
+ "open": &objects.UserFunction{Name: "open", Value: osOpen}, // open(name string) => imap(file)/error
+ "open_file": &objects.UserFunction{Name: "open_file", Value: osOpenFile}, // open_file(name string, flag int, perm int) => imap(file)/error
+ "find_process": &objects.UserFunction{Name: "find_process", Value: osFindProcess}, // find_process(pid int) => imap(process)/error
+ "start_process": &objects.UserFunction{Name: "start_process", Value: osStartProcess}, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error
+ "exec_look_path": &objects.UserFunction{Name: "exec_look_path", Value: FuncASRSE(exec.LookPath)}, // exec_look_path(file) => string/error
+ "exec": &objects.UserFunction{Name: "exec", Value: osExec}, // exec(name, args...) => command
+ "stat": &objects.UserFunction{Name: "stat", Value: osStat}, // stat(name) => imap(fileinfo)/error
+ "read_file": &objects.UserFunction{Name: "read_file", Value: osReadFile}, // readfile(name) => array(byte)/error
+}
+
+func osReadFile(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ fname, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ bytes, err := ioutil.ReadFile(fname)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ if len(bytes) > tengo.MaxBytesLen {
+ return nil, objects.ErrBytesLimit
+ }
+
+ return &objects.Bytes{Value: bytes}, nil
+}
+
+func osStat(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ fname, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ stat, err := os.Stat(fname)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ fstat := &objects.ImmutableMap{
+ Value: map[string]objects.Object{
+ "name": &objects.String{Value: stat.Name()},
+ "mtime": &objects.Time{Value: stat.ModTime()},
+ "size": &objects.Int{Value: stat.Size()},
+ "mode": &objects.Int{Value: int64(stat.Mode())},
+ },
+ }
+
+ if stat.IsDir() {
+ fstat.Value["directory"] = objects.TrueValue
+ } else {
+ fstat.Value["directory"] = objects.FalseValue
+ }
+
+ return fstat, nil
+}
+
+func osCreate(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, err := os.Create(s1)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return makeOSFile(res), nil
+}
+
+func osOpen(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, err := os.Open(s1)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return makeOSFile(res), nil
+}
+
+func osOpenFile(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 3 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ i3, ok := objects.ToInt(args[2])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ }
+
+ res, err := os.OpenFile(s1, i2, os.FileMode(i3))
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return makeOSFile(res), nil
+}
+
+func osArgs(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ arr := &objects.Array{}
+ for _, osArg := range os.Args {
+ if len(osArg) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ arr.Value = append(arr.Value, &objects.String{Value: osArg})
+ }
+
+ return arr, nil
+}
+
+func osFuncASFmRE(name string, fn func(string, os.FileMode) error) *objects.UserFunction {
+ return &objects.UserFunction{
+ Name: name,
+ Value: func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+ i2, ok := objects.ToInt64(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ return wrapError(fn(s1, os.FileMode(i2))), nil
+ },
+ }
+}
+
+func osLookupEnv(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, ok := os.LookupEnv(s1)
+ if !ok {
+ return objects.FalseValue, nil
+ }
+
+ if len(res) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: res}, nil
+}
+
+func osExpandEnv(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ var vlen int
+ var failed bool
+ s := os.Expand(s1, func(k string) string {
+ if failed {
+ return ""
+ }
+
+ v := os.Getenv(k)
+
+ // this does not count the other texts that are not being replaced
+ // but the code checks the final length at the end
+ vlen += len(v)
+ if vlen > tengo.MaxStringLen {
+ failed = true
+ return ""
+ }
+
+ return v
+ })
+
+ if failed || len(s) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: s}, nil
+}
+
+func osExec(args ...objects.Object) (objects.Object, error) {
+ if len(args) == 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ name, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ var execArgs []string
+ for idx, arg := range args[1:] {
+ execArg, ok := objects.ToString(arg)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: fmt.Sprintf("args[%d]", idx),
+ Expected: "string(compatible)",
+ Found: args[1+idx].TypeName(),
+ }
+ }
+
+ execArgs = append(execArgs, execArg)
+ }
+
+ return makeOSExecCommand(exec.Command(name, execArgs...)), nil
+}
+
+func osFindProcess(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ proc, err := os.FindProcess(i1)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return makeOSProcess(proc), nil
+}
+
+func osStartProcess(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 4 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ name, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ var argv []string
+ var err error
+ switch arg1 := args[1].(type) {
+ case *objects.Array:
+ argv, err = stringArray(arg1.Value, "second")
+ if err != nil {
+ return nil, err
+ }
+ case *objects.ImmutableArray:
+ argv, err = stringArray(arg1.Value, "second")
+ if err != nil {
+ return nil, err
+ }
+ default:
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "array",
+ Found: arg1.TypeName(),
+ }
+ }
+
+ dir, ok := objects.ToString(args[2])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "string(compatible)",
+ Found: args[2].TypeName(),
+ }
+ }
+
+ var env []string
+ switch arg3 := args[3].(type) {
+ case *objects.Array:
+ env, err = stringArray(arg3.Value, "fourth")
+ if err != nil {
+ return nil, err
+ }
+ case *objects.ImmutableArray:
+ env, err = stringArray(arg3.Value, "fourth")
+ if err != nil {
+ return nil, err
+ }
+ default:
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "fourth",
+ Expected: "array",
+ Found: arg3.TypeName(),
+ }
+ }
+
+ proc, err := os.StartProcess(name, argv, &os.ProcAttr{
+ Dir: dir,
+ Env: env,
+ })
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return makeOSProcess(proc), nil
+}
+
+func stringArray(arr []objects.Object, argName string) ([]string, error) {
+ var sarr []string
+ for idx, elem := range arr {
+ str, ok := elem.(*objects.String)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: fmt.Sprintf("%s[%d]", argName, idx),
+ Expected: "string",
+ Found: elem.TypeName(),
+ }
+ }
+
+ sarr = append(sarr, str.Value)
+ }
+
+ return sarr, nil
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/os_exec.go b/vendor/github.com/d5/tengo/stdlib/os_exec.go
new file mode 100644
index 00000000..5274c36a
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/os_exec.go
@@ -0,0 +1,113 @@
+package stdlib
+
+import (
+ "os/exec"
+
+ "github.com/d5/tengo/objects"
+)
+
+func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap {
+ return &objects.ImmutableMap{
+ Value: map[string]objects.Object{
+ // combined_output() => bytes/error
+ "combined_output": &objects.UserFunction{Name: "combined_output", Value: FuncARYE(cmd.CombinedOutput)}, //
+ // output() => bytes/error
+ "output": &objects.UserFunction{Name: "output", Value: FuncARYE(cmd.Output)}, //
+ // run() => error
+ "run": &objects.UserFunction{Name: "run", Value: FuncARE(cmd.Run)}, //
+ // start() => error
+ "start": &objects.UserFunction{Name: "start", Value: FuncARE(cmd.Start)}, //
+ // wait() => error
+ "wait": &objects.UserFunction{Name: "wait", Value: FuncARE(cmd.Wait)}, //
+ // set_path(path string)
+ "set_path": &objects.UserFunction{
+ Name: "set_path",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ cmd.Path = s1
+
+ return objects.UndefinedValue, nil
+ },
+ },
+ // set_dir(dir string)
+ "set_dir": &objects.UserFunction{
+ Name: "set_dir",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ cmd.Dir = s1
+
+ return objects.UndefinedValue, nil
+ },
+ },
+ // set_env(env array(string))
+ "set_env": &objects.UserFunction{
+ Name: "set_env",
+ Value: func(args ...objects.Object) (objects.Object, error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ var env []string
+ var err error
+ switch arg0 := args[0].(type) {
+ case *objects.Array:
+ env, err = stringArray(arg0.Value, "first")
+ if err != nil {
+ return nil, err
+ }
+ case *objects.ImmutableArray:
+ env, err = stringArray(arg0.Value, "first")
+ if err != nil {
+ return nil, err
+ }
+ default:
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "array",
+ Found: arg0.TypeName(),
+ }
+ }
+
+ cmd.Env = env
+
+ return objects.UndefinedValue, nil
+ },
+ },
+ // process() => imap(process)
+ "process": &objects.UserFunction{
+ Name: "process",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ return makeOSProcess(cmd.Process), nil
+ },
+ },
+ },
+ }
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/os_file.go b/vendor/github.com/d5/tengo/stdlib/os_file.go
new file mode 100644
index 00000000..ee9f625a
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/os_file.go
@@ -0,0 +1,96 @@
+package stdlib
+
+import (
+ "os"
+
+ "github.com/d5/tengo/objects"
+)
+
+func makeOSFile(file *os.File) *objects.ImmutableMap {
+ return &objects.ImmutableMap{
+ Value: map[string]objects.Object{
+ // chdir() => true/error
+ "chdir": &objects.UserFunction{Name: "chdir", Value: FuncARE(file.Chdir)}, //
+ // chown(uid int, gid int) => true/error
+ "chown": &objects.UserFunction{Name: "chown", Value: FuncAIIRE(file.Chown)}, //
+ // close() => error
+ "close": &objects.UserFunction{Name: "close", Value: FuncARE(file.Close)}, //
+ // name() => string
+ "name": &objects.UserFunction{Name: "name", Value: FuncARS(file.Name)}, //
+ // readdirnames(n int) => array(string)/error
+ "readdirnames": &objects.UserFunction{Name: "readdirnames", Value: FuncAIRSsE(file.Readdirnames)}, //
+ // sync() => error
+ "sync": &objects.UserFunction{Name: "sync", Value: FuncARE(file.Sync)}, //
+ // write(bytes) => int/error
+ "write": &objects.UserFunction{Name: "write", Value: FuncAYRIE(file.Write)}, //
+ // write(string) => int/error
+ "write_string": &objects.UserFunction{Name: "write_string", Value: FuncASRIE(file.WriteString)}, //
+ // read(bytes) => int/error
+ "read": &objects.UserFunction{Name: "read", Value: FuncAYRIE(file.Read)}, //
+ // chmod(mode int) => error
+ "chmod": &objects.UserFunction{
+ Name: "chmod",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ return wrapError(file.Chmod(os.FileMode(i1))), nil
+ },
+ },
+ // seek(offset int, whence int) => int/error
+ "seek": &objects.UserFunction{
+ Name: "seek",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ res, err := file.Seek(i1, i2)
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return &objects.Int{Value: res}, nil
+ },
+ },
+ // stat() => imap(fileinfo)/error
+ "stat": &objects.UserFunction{
+ Name: "start",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ return osStat(&objects.String{Value: file.Name()})
+ },
+ },
+ },
+ }
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/os_process.go b/vendor/github.com/d5/tengo/stdlib/os_process.go
new file mode 100644
index 00000000..801ccdef
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/os_process.go
@@ -0,0 +1,62 @@
+package stdlib
+
+import (
+ "os"
+ "syscall"
+
+ "github.com/d5/tengo/objects"
+)
+
+func makeOSProcessState(state *os.ProcessState) *objects.ImmutableMap {
+ return &objects.ImmutableMap{
+ Value: map[string]objects.Object{
+ "exited": &objects.UserFunction{Name: "exited", Value: FuncARB(state.Exited)}, //
+ "pid": &objects.UserFunction{Name: "pid", Value: FuncARI(state.Pid)}, //
+ "string": &objects.UserFunction{Name: "string", Value: FuncARS(state.String)}, //
+ "success": &objects.UserFunction{Name: "success", Value: FuncARB(state.Success)}, //
+ },
+ }
+}
+
+func makeOSProcess(proc *os.Process) *objects.ImmutableMap {
+ return &objects.ImmutableMap{
+ Value: map[string]objects.Object{
+ "kill": &objects.UserFunction{Name: "kill", Value: FuncARE(proc.Kill)}, //
+ "release": &objects.UserFunction{Name: "release", Value: FuncARE(proc.Release)}, //
+ "signal": &objects.UserFunction{
+ Name: "signal",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ return wrapError(proc.Signal(syscall.Signal(i1))), nil
+ },
+ },
+ "wait": &objects.UserFunction{
+ Name: "wait",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ state, err := proc.Wait()
+ if err != nil {
+ return wrapError(err), nil
+ }
+
+ return makeOSProcessState(state), nil
+ },
+ },
+ },
+ }
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/rand.go b/vendor/github.com/d5/tengo/stdlib/rand.go
new file mode 100644
index 00000000..6efe1de8
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/rand.go
@@ -0,0 +1,102 @@
+package stdlib
+
+import (
+ "math/rand"
+
+ "github.com/d5/tengo/objects"
+)
+
+var randModule = map[string]objects.Object{
+ "int": &objects.UserFunction{Name: "int", Value: FuncARI64(rand.Int63)},
+ "float": &objects.UserFunction{Name: "float", Value: FuncARF(rand.Float64)},
+ "intn": &objects.UserFunction{Name: "intn", Value: FuncAI64RI64(rand.Int63n)},
+ "exp_float": &objects.UserFunction{Name: "exp_float", Value: FuncARF(rand.ExpFloat64)},
+ "norm_float": &objects.UserFunction{Name: "norm_float", Value: FuncARF(rand.NormFloat64)},
+ "perm": &objects.UserFunction{Name: "perm", Value: FuncAIRIs(rand.Perm)},
+ "seed": &objects.UserFunction{Name: "seed", Value: FuncAI64R(rand.Seed)},
+ "read": &objects.UserFunction{
+ Name: "read",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ y1, ok := args[0].(*objects.Bytes)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "bytes",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, err := rand.Read(y1.Value)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ return &objects.Int{Value: int64(res)}, nil
+ },
+ },
+ "rand": &objects.UserFunction{
+ Name: "rand",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ src := rand.NewSource(i1)
+
+ return randRand(rand.New(src)), nil
+ },
+ },
+}
+
+func randRand(r *rand.Rand) *objects.ImmutableMap {
+ return &objects.ImmutableMap{
+ Value: map[string]objects.Object{
+ "int": &objects.UserFunction{Name: "int", Value: FuncARI64(r.Int63)},
+ "float": &objects.UserFunction{Name: "float", Value: FuncARF(r.Float64)},
+ "intn": &objects.UserFunction{Name: "intn", Value: FuncAI64RI64(r.Int63n)},
+ "exp_float": &objects.UserFunction{Name: "exp_float", Value: FuncARF(r.ExpFloat64)},
+ "norm_float": &objects.UserFunction{Name: "norm_float", Value: FuncARF(r.NormFloat64)},
+ "perm": &objects.UserFunction{Name: "perm", Value: FuncAIRIs(r.Perm)},
+ "seed": &objects.UserFunction{Name: "seed", Value: FuncAI64R(r.Seed)},
+ "read": &objects.UserFunction{
+ Name: "read",
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ y1, ok := args[0].(*objects.Bytes)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "bytes",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ res, err := r.Read(y1.Value)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ return &objects.Int{Value: int64(res)}, nil
+ },
+ },
+ },
+ }
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/source_modules.go b/vendor/github.com/d5/tengo/stdlib/source_modules.go
new file mode 100644
index 00000000..ca69d7d1
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/source_modules.go
@@ -0,0 +1,8 @@
+// Code generated using gensrcmods.go; DO NOT EDIT.
+
+package stdlib
+
+// SourceModules are source type standard library modules.
+var SourceModules = map[string]string{
+ "enum": "is_enumerable := func(x) {\n return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x)\n}\n\nis_array_like := func(x) {\n return is_array(x) || is_immutable_array(x)\n}\n\nexport {\n // all returns true if the given function `fn` evaluates to a truthy value on\n // all of the items in `x`. It returns undefined if `x` is not enumerable.\n all: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if !fn(k, v) { return false }\n }\n\n return true\n },\n // any returns true if the given function `fn` evaluates to a truthy value on\n // any of the items in `x`. It returns undefined if `x` is not enumerable.\n any: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return true }\n }\n\n return false\n },\n // chunk returns an array of elements split into groups the length of size.\n // If `x` can't be split evenly, the final chunk will be the remaining elements.\n // It returns undefined if `x` is not array.\n chunk: func(x, size) {\n if !is_array_like(x) || !size { return undefined }\n\n numElements := len(x)\n if !numElements { return [] }\n\n res := []\n idx := 0\n for idx < numElements {\n res = append(res, x[idx:idx+size])\n idx += size\n }\n\n return res\n },\n // at returns an element at the given index (if `x` is array) or\n // key (if `x` is map). It returns undefined if `x` is not enumerable.\n at: func(x, key) {\n if !is_enumerable(x) { return undefined }\n\n if is_array_like(x) {\n if !is_int(key) { return undefined }\n } else {\n if !is_string(key) { return undefined }\n }\n\n return x[key]\n },\n // each iterates over elements of `x` and invokes `fn` for each element. `fn` is\n // invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It does not iterate\n // and returns undefined if `x` is not enumerable.\n each: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n fn(k, v)\n }\n },\n // filter iterates over elements of `x`, returning an array of all elements `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n filter: func(x, fn) {\n if !is_array_like(x) { return undefined }\n\n dst := []\n for k, v in x {\n if fn(k, v) { dst = append(dst, v) }\n }\n\n return dst\n },\n // find iterates over elements of `x`, returning value of the first element `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n find: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return v }\n }\n },\n // find_key iterates over elements of `x`, returning key or index of the first\n // element `fn` returns truthy for. `fn` is invoked with two arguments: `key`\n // and `value`. `key` is an int index if `x` is array. `key` is a string key if\n // `x` is map. It returns undefined if `x` is not enumerable.\n find_key: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return k }\n }\n },\n // map creates an array of values by running each element in `x` through `fn`.\n // `fn` is invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It returns undefined\n // if `x` is not enumerable.\n map: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n dst := []\n for k, v in x {\n dst = append(dst, fn(k, v))\n }\n\n return dst\n },\n // key returns the first argument.\n key: func(k, _) { return k },\n // value returns the second argument.\n value: func(_, v) { return v }\n}\n",
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo b/vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo
new file mode 100644
index 00000000..7a5ea637
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo
@@ -0,0 +1,128 @@
+is_enumerable := func(x) {
+ return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x)
+}
+
+is_array_like := func(x) {
+ return is_array(x) || is_immutable_array(x)
+}
+
+export {
+ // all returns true if the given function `fn` evaluates to a truthy value on
+ // all of the items in `x`. It returns undefined if `x` is not enumerable.
+ all: func(x, fn) {
+ if !is_enumerable(x) { return undefined }
+
+ for k, v in x {
+ if !fn(k, v) { return false }
+ }
+
+ return true
+ },
+ // any returns true if the given function `fn` evaluates to a truthy value on
+ // any of the items in `x`. It returns undefined if `x` is not enumerable.
+ any: func(x, fn) {
+ if !is_enumerable(x) { return undefined }
+
+ for k, v in x {
+ if fn(k, v) { return true }
+ }
+
+ return false
+ },
+ // chunk returns an array of elements split into groups the length of size.
+ // If `x` can't be split evenly, the final chunk will be the remaining elements.
+ // It returns undefined if `x` is not array.
+ chunk: func(x, size) {
+ if !is_array_like(x) || !size { return undefined }
+
+ numElements := len(x)
+ if !numElements { return [] }
+
+ res := []
+ idx := 0
+ for idx < numElements {
+ res = append(res, x[idx:idx+size])
+ idx += size
+ }
+
+ return res
+ },
+ // at returns an element at the given index (if `x` is array) or
+ // key (if `x` is map). It returns undefined if `x` is not enumerable.
+ at: func(x, key) {
+ if !is_enumerable(x) { return undefined }
+
+ if is_array_like(x) {
+ if !is_int(key) { return undefined }
+ } else {
+ if !is_string(key) { return undefined }
+ }
+
+ return x[key]
+ },
+ // each iterates over elements of `x` and invokes `fn` for each element. `fn` is
+ // invoked with two arguments: `key` and `value`. `key` is an int index
+ // if `x` is array. `key` is a string key if `x` is map. It does not iterate
+ // and returns undefined if `x` is not enumerable.
+ each: func(x, fn) {
+ if !is_enumerable(x) { return undefined }
+
+ for k, v in x {
+ fn(k, v)
+ }
+ },
+ // filter iterates over elements of `x`, returning an array of all elements `fn`
+ // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.
+ // `key` is an int index if `x` is array. `key` is a string key if `x` is map.
+ // It returns undefined if `x` is not enumerable.
+ filter: func(x, fn) {
+ if !is_array_like(x) { return undefined }
+
+ dst := []
+ for k, v in x {
+ if fn(k, v) { dst = append(dst, v) }
+ }
+
+ return dst
+ },
+ // find iterates over elements of `x`, returning value of the first element `fn`
+ // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.
+ // `key` is an int index if `x` is array. `key` is a string key if `x` is map.
+ // It returns undefined if `x` is not enumerable.
+ find: func(x, fn) {
+ if !is_enumerable(x) { return undefined }
+
+ for k, v in x {
+ if fn(k, v) { return v }
+ }
+ },
+ // find_key iterates over elements of `x`, returning key or index of the first
+ // element `fn` returns truthy for. `fn` is invoked with two arguments: `key`
+ // and `value`. `key` is an int index if `x` is array. `key` is a string key if
+ // `x` is map. It returns undefined if `x` is not enumerable.
+ find_key: func(x, fn) {
+ if !is_enumerable(x) { return undefined }
+
+ for k, v in x {
+ if fn(k, v) { return k }
+ }
+ },
+ // map creates an array of values by running each element in `x` through `fn`.
+ // `fn` is invoked with two arguments: `key` and `value`. `key` is an int index
+ // if `x` is array. `key` is a string key if `x` is map. It returns undefined
+ // if `x` is not enumerable.
+ map: func(x, fn) {
+ if !is_enumerable(x) { return undefined }
+
+ dst := []
+ for k, v in x {
+ dst = append(dst, fn(k, v))
+ }
+
+ return dst
+ },
+ // key returns the first argument.
+ key: func(k, _) { return k },
+ // value returns the second argument.
+ value: func(_, v) { return v }
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/stdlib.go b/vendor/github.com/d5/tengo/stdlib/stdlib.go
new file mode 100644
index 00000000..aad220ee
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/stdlib.go
@@ -0,0 +1,34 @@
+package stdlib
+
+//go:generate go run gensrcmods.go
+
+import "github.com/d5/tengo/objects"
+
+// AllModuleNames returns a list of all default module names.
+func AllModuleNames() []string {
+ var names []string
+ for name := range BuiltinModules {
+ names = append(names, name)
+ }
+ for name := range SourceModules {
+ names = append(names, name)
+ }
+ return names
+}
+
+// GetModuleMap returns the module map that includes all modules
+// for the given module names.
+func GetModuleMap(names ...string) *objects.ModuleMap {
+ modules := objects.NewModuleMap()
+
+ for _, name := range names {
+ if mod := BuiltinModules[name]; mod != nil {
+ modules.AddBuiltinModule(name, mod)
+ }
+ if mod := SourceModules[name]; mod != "" {
+ modules.AddSourceModule(name, []byte(mod))
+ }
+ }
+
+ return modules
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/text.go b/vendor/github.com/d5/tengo/stdlib/text.go
new file mode 100644
index 00000000..9f9770b8
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/text.go
@@ -0,0 +1,737 @@
+package stdlib
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/d5/tengo"
+ "github.com/d5/tengo/objects"
+)
+
+var textModule = map[string]objects.Object{
+ "re_match": &objects.UserFunction{Name: "re_match", Value: textREMatch}, // re_match(pattern, text) => bool/error
+ "re_find": &objects.UserFunction{Name: "re_find", Value: textREFind}, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined
+ "re_replace": &objects.UserFunction{Name: "re_replace", Value: textREReplace}, // re_replace(pattern, text, repl) => string/error
+ "re_split": &objects.UserFunction{Name: "re_split", Value: textRESplit}, // re_split(pattern, text, count) => [string]/error
+ "re_compile": &objects.UserFunction{Name: "re_compile", Value: textRECompile}, // re_compile(pattern) => Regexp/error
+ "compare": &objects.UserFunction{Name: "compare", Value: FuncASSRI(strings.Compare)}, // compare(a, b) => int
+ "contains": &objects.UserFunction{Name: "contains", Value: FuncASSRB(strings.Contains)}, // contains(s, substr) => bool
+ "contains_any": &objects.UserFunction{Name: "contains_any", Value: FuncASSRB(strings.ContainsAny)}, // contains_any(s, chars) => bool
+ "count": &objects.UserFunction{Name: "count", Value: FuncASSRI(strings.Count)}, // count(s, substr) => int
+ "equal_fold": &objects.UserFunction{Name: "equal_fold", Value: FuncASSRB(strings.EqualFold)}, // "equal_fold(s, t) => bool
+ "fields": &objects.UserFunction{Name: "fields", Value: FuncASRSs(strings.Fields)}, // fields(s) => [string]
+ "has_prefix": &objects.UserFunction{Name: "has_prefix", Value: FuncASSRB(strings.HasPrefix)}, // has_prefix(s, prefix) => bool
+ "has_suffix": &objects.UserFunction{Name: "has_suffix", Value: FuncASSRB(strings.HasSuffix)}, // has_suffix(s, suffix) => bool
+ "index": &objects.UserFunction{Name: "index", Value: FuncASSRI(strings.Index)}, // index(s, substr) => int
+ "index_any": &objects.UserFunction{Name: "index_any", Value: FuncASSRI(strings.IndexAny)}, // index_any(s, chars) => int
+ "join": &objects.UserFunction{Name: "join", Value: textJoin}, // join(arr, sep) => string
+ "last_index": &objects.UserFunction{Name: "last_index", Value: FuncASSRI(strings.LastIndex)}, // last_index(s, substr) => int
+ "last_index_any": &objects.UserFunction{Name: "last_index_any", Value: FuncASSRI(strings.LastIndexAny)}, // last_index_any(s, chars) => int
+ "repeat": &objects.UserFunction{Name: "repeat", Value: textRepeat}, // repeat(s, count) => string
+ "replace": &objects.UserFunction{Name: "replace", Value: textReplace}, // replace(s, old, new, n) => string
+ "split": &objects.UserFunction{Name: "split", Value: FuncASSRSs(strings.Split)}, // split(s, sep) => [string]
+ "split_after": &objects.UserFunction{Name: "split_after", Value: FuncASSRSs(strings.SplitAfter)}, // split_after(s, sep) => [string]
+ "split_after_n": &objects.UserFunction{Name: "split_after_n", Value: FuncASSIRSs(strings.SplitAfterN)}, // split_after_n(s, sep, n) => [string]
+ "split_n": &objects.UserFunction{Name: "split_n", Value: FuncASSIRSs(strings.SplitN)}, // split_n(s, sep, n) => [string]
+ "title": &objects.UserFunction{Name: "title", Value: FuncASRS(strings.Title)}, // title(s) => string
+ "to_lower": &objects.UserFunction{Name: "to_lower", Value: FuncASRS(strings.ToLower)}, // to_lower(s) => string
+ "to_title": &objects.UserFunction{Name: "to_title", Value: FuncASRS(strings.ToTitle)}, // to_title(s) => string
+ "to_upper": &objects.UserFunction{Name: "to_upper", Value: FuncASRS(strings.ToUpper)}, // to_upper(s) => string
+ "trim_left": &objects.UserFunction{Name: "trim_left", Value: FuncASSRS(strings.TrimLeft)}, // trim_left(s, cutset) => string
+ "trim_prefix": &objects.UserFunction{Name: "trim_prefix", Value: FuncASSRS(strings.TrimPrefix)}, // trim_prefix(s, prefix) => string
+ "trim_right": &objects.UserFunction{Name: "trim_right", Value: FuncASSRS(strings.TrimRight)}, // trim_right(s, cutset) => string
+ "trim_space": &objects.UserFunction{Name: "trim_space", Value: FuncASRS(strings.TrimSpace)}, // trim_space(s) => string
+ "trim_suffix": &objects.UserFunction{Name: "trim_suffix", Value: FuncASSRS(strings.TrimSuffix)}, // trim_suffix(s, suffix) => string
+ "atoi": &objects.UserFunction{Name: "atoi", Value: FuncASRIE(strconv.Atoi)}, // atoi(str) => int/error
+ "format_bool": &objects.UserFunction{Name: "format_bool", Value: textFormatBool}, // format_bool(b) => string
+ "format_float": &objects.UserFunction{Name: "format_float", Value: textFormatFloat}, // format_float(f, fmt, prec, bits) => string
+ "format_int": &objects.UserFunction{Name: "format_int", Value: textFormatInt}, // format_int(i, base) => string
+ "itoa": &objects.UserFunction{Name: "itoa", Value: FuncAIRS(strconv.Itoa)}, // itoa(i) => string
+ "parse_bool": &objects.UserFunction{Name: "parse_bool", Value: textParseBool}, // parse_bool(str) => bool/error
+ "parse_float": &objects.UserFunction{Name: "parse_float", Value: textParseFloat}, // parse_float(str, bits) => float/error
+ "parse_int": &objects.UserFunction{Name: "parse_int", Value: textParseInt}, // parse_int(str, base, bits) => int/error
+ "quote": &objects.UserFunction{Name: "quote", Value: FuncASRS(strconv.Quote)}, // quote(str) => string
+ "unquote": &objects.UserFunction{Name: "unquote", Value: FuncASRSE(strconv.Unquote)}, // unquote(str) => string/error
+}
+
+func textREMatch(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ matched, err := regexp.MatchString(s1, s2)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ if matched {
+ ret = objects.TrueValue
+ } else {
+ ret = objects.FalseValue
+ }
+
+ return
+}
+
+func textREFind(args ...objects.Object) (ret objects.Object, err error) {
+ numArgs := len(args)
+ if numArgs != 2 && numArgs != 3 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ re, err := regexp.Compile(s1)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ if numArgs < 3 {
+ m := re.FindStringSubmatchIndex(s2)
+ if m == nil {
+ ret = objects.UndefinedValue
+ return
+ }
+
+ arr := &objects.Array{}
+ for i := 0; i < len(m); i += 2 {
+ arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
+ "text": &objects.String{Value: s2[m[i]:m[i+1]]},
+ "begin": &objects.Int{Value: int64(m[i])},
+ "end": &objects.Int{Value: int64(m[i+1])},
+ }})
+ }
+
+ ret = &objects.Array{Value: []objects.Object{arr}}
+
+ return
+ }
+
+ i3, ok := objects.ToInt(args[2])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+ m := re.FindAllStringSubmatchIndex(s2, i3)
+ if m == nil {
+ ret = objects.UndefinedValue
+ return
+ }
+
+ arr := &objects.Array{}
+ for _, m := range m {
+ subMatch := &objects.Array{}
+ for i := 0; i < len(m); i += 2 {
+ subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
+ "text": &objects.String{Value: s2[m[i]:m[i+1]]},
+ "begin": &objects.Int{Value: int64(m[i])},
+ "end": &objects.Int{Value: int64(m[i+1])},
+ }})
+ }
+
+ arr.Value = append(arr.Value, subMatch)
+ }
+
+ ret = arr
+
+ return
+}
+
+func textREReplace(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 3 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ s3, ok := objects.ToString(args[2])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "string(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ re, err := regexp.Compile(s1)
+ if err != nil {
+ ret = wrapError(err)
+ } else {
+ s, ok := doTextRegexpReplace(re, s2, s3)
+ if !ok {
+ return nil, objects.ErrStringLimit
+ }
+
+ ret = &objects.String{Value: s}
+ }
+
+ return
+}
+
+func textRESplit(args ...objects.Object) (ret objects.Object, err error) {
+ numArgs := len(args)
+ if numArgs != 2 && numArgs != 3 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ var i3 = -1
+ if numArgs > 2 {
+ i3, ok = objects.ToInt(args[2])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+ }
+
+ re, err := regexp.Compile(s1)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ arr := &objects.Array{}
+ for _, s := range re.Split(s2, i3) {
+ arr.Value = append(arr.Value, &objects.String{Value: s})
+ }
+
+ ret = arr
+
+ return
+}
+
+func textRECompile(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ re, err := regexp.Compile(s1)
+ if err != nil {
+ ret = wrapError(err)
+ } else {
+ ret = makeTextRegexp(re)
+ }
+
+ return
+}
+
+func textReplace(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 4 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ s3, ok := objects.ToString(args[2])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "string(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ i4, ok := objects.ToInt(args[3])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "fourth",
+ Expected: "int(compatible)",
+ Found: args[3].TypeName(),
+ }
+ return
+ }
+
+ s, ok := doTextReplace(s1, s2, s3, i4)
+ if !ok {
+ err = objects.ErrStringLimit
+ return
+ }
+
+ ret = &objects.String{Value: s}
+
+ return
+}
+
+func textRepeat(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ if len(s1)*i2 > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: strings.Repeat(s1, i2)}, nil
+}
+
+func textJoin(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ return nil, objects.ErrWrongNumArguments
+ }
+
+ var slen int
+ var ss1 []string
+ switch arg0 := args[0].(type) {
+ case *objects.Array:
+ for idx, a := range arg0.Value {
+ as, ok := objects.ToString(a)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: fmt.Sprintf("first[%d]", idx),
+ Expected: "string(compatible)",
+ Found: a.TypeName(),
+ }
+ }
+ slen += len(as)
+ ss1 = append(ss1, as)
+ }
+ case *objects.ImmutableArray:
+ for idx, a := range arg0.Value {
+ as, ok := objects.ToString(a)
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: fmt.Sprintf("first[%d]", idx),
+ Expected: "string(compatible)",
+ Found: a.TypeName(),
+ }
+ }
+ slen += len(as)
+ ss1 = append(ss1, as)
+ }
+ default:
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "array",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ return nil, objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ // make sure output length does not exceed the limit
+ if slen+len(s2)*(len(ss1)-1) > tengo.MaxStringLen {
+ return nil, objects.ErrStringLimit
+ }
+
+ return &objects.String{Value: strings.Join(ss1, s2)}, nil
+}
+
+func textFormatBool(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ b1, ok := args[0].(*objects.Bool)
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "bool",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ if b1 == objects.TrueValue {
+ ret = &objects.String{Value: "true"}
+ } else {
+ ret = &objects.String{Value: "false"}
+ }
+
+ return
+}
+
+func textFormatFloat(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 4 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ f1, ok := args[0].(*objects.Float)
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "float",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ i3, ok := objects.ToInt(args[2])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ i4, ok := objects.ToInt(args[3])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "fourth",
+ Expected: "int(compatible)",
+ Found: args[3].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)}
+
+ return
+}
+
+func textFormatInt(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := args[0].(*objects.Int)
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.String{Value: strconv.FormatInt(i1.Value, i2)}
+
+ return
+}
+
+func textParseBool(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := args[0].(*objects.String)
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ parsed, err := strconv.ParseBool(s1.Value)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ if parsed {
+ ret = objects.TrueValue
+ } else {
+ ret = objects.FalseValue
+ }
+
+ return
+}
+
+func textParseFloat(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := args[0].(*objects.String)
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ parsed, err := strconv.ParseFloat(s1.Value, i2)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ ret = &objects.Float{Value: parsed}
+
+ return
+}
+
+func textParseInt(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 3 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := args[0].(*objects.String)
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ i3, ok := objects.ToInt(args[2])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ parsed, err := strconv.ParseInt(s1.Value, i2, i3)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ ret = &objects.Int{Value: parsed}
+
+ return
+}
+
+// Modified implementation of strings.Replace
+// to limit the maximum length of output string.
+func doTextReplace(s, old, new string, n int) (string, bool) {
+ if old == new || n == 0 {
+ return s, true // avoid allocation
+ }
+
+ // Compute number of replacements.
+ if m := strings.Count(s, old); m == 0 {
+ return s, true // avoid allocation
+ } else if n < 0 || m < n {
+ n = m
+ }
+
+ // Apply replacements to buffer.
+ t := make([]byte, len(s)+n*(len(new)-len(old)))
+ w := 0
+ start := 0
+ for i := 0; i < n; i++ {
+ j := start
+ if len(old) == 0 {
+ if i > 0 {
+ _, wid := utf8.DecodeRuneInString(s[start:])
+ j += wid
+ }
+ } else {
+ j += strings.Index(s[start:], old)
+ }
+
+ ssj := s[start:j]
+ if w+len(ssj)+len(new) > tengo.MaxStringLen {
+ return "", false
+ }
+
+ w += copy(t[w:], ssj)
+ w += copy(t[w:], new)
+ start = j + len(old)
+ }
+
+ ss := s[start:]
+ if w+len(ss) > tengo.MaxStringLen {
+ return "", false
+ }
+
+ w += copy(t[w:], ss)
+
+ return string(t[0:w]), true
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/text_regexp.go b/vendor/github.com/d5/tengo/stdlib/text_regexp.go
new file mode 100644
index 00000000..16f135bf
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/text_regexp.go
@@ -0,0 +1,228 @@
+package stdlib
+
+import (
+ "regexp"
+
+ "github.com/d5/tengo"
+ "github.com/d5/tengo/objects"
+)
+
+func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap {
+ return &objects.ImmutableMap{
+ Value: map[string]objects.Object{
+ // match(text) => bool
+ "match": &objects.UserFunction{
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ if re.MatchString(s1) {
+ ret = objects.TrueValue
+ } else {
+ ret = objects.FalseValue
+ }
+
+ return
+ },
+ },
+
+ // find(text) => array(array({text:,begin:,end:}))/undefined
+ // find(text, maxCount) => array(array({text:,begin:,end:}))/undefined
+ "find": &objects.UserFunction{
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ numArgs := len(args)
+ if numArgs != 1 && numArgs != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ if numArgs == 1 {
+ m := re.FindStringSubmatchIndex(s1)
+ if m == nil {
+ ret = objects.UndefinedValue
+ return
+ }
+
+ arr := &objects.Array{}
+ for i := 0; i < len(m); i += 2 {
+ arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
+ "text": &objects.String{Value: s1[m[i]:m[i+1]]},
+ "begin": &objects.Int{Value: int64(m[i])},
+ "end": &objects.Int{Value: int64(m[i+1])},
+ }})
+ }
+
+ ret = &objects.Array{Value: []objects.Object{arr}}
+
+ return
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+ m := re.FindAllStringSubmatchIndex(s1, i2)
+ if m == nil {
+ ret = objects.UndefinedValue
+ return
+ }
+
+ arr := &objects.Array{}
+ for _, m := range m {
+ subMatch := &objects.Array{}
+ for i := 0; i < len(m); i += 2 {
+ subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
+ "text": &objects.String{Value: s1[m[i]:m[i+1]]},
+ "begin": &objects.Int{Value: int64(m[i])},
+ "end": &objects.Int{Value: int64(m[i+1])},
+ }})
+ }
+
+ arr.Value = append(arr.Value, subMatch)
+ }
+
+ ret = arr
+
+ return
+ },
+ },
+
+ // replace(src, repl) => string
+ "replace": &objects.UserFunction{
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ s, ok := doTextRegexpReplace(re, s1, s2)
+ if !ok {
+ return nil, objects.ErrStringLimit
+ }
+
+ ret = &objects.String{Value: s}
+
+ return
+ },
+ },
+
+ // split(text) => array(string)
+ // split(text, maxCount) => array(string)
+ "split": &objects.UserFunction{
+ Value: func(args ...objects.Object) (ret objects.Object, err error) {
+ numArgs := len(args)
+ if numArgs != 1 && numArgs != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ var i2 = -1
+ if numArgs > 1 {
+ i2, ok = objects.ToInt(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+ }
+
+ arr := &objects.Array{}
+ for _, s := range re.Split(s1, i2) {
+ arr.Value = append(arr.Value, &objects.String{Value: s})
+ }
+
+ ret = arr
+
+ return
+ },
+ },
+ },
+ }
+}
+
+// Size-limit checking implementation of regexp.ReplaceAllString.
+func doTextRegexpReplace(re *regexp.Regexp, src, repl string) (string, bool) {
+ idx := 0
+ out := ""
+
+ for _, m := range re.FindAllStringSubmatchIndex(src, -1) {
+ var exp []byte
+ exp = re.ExpandString(exp, repl, src, m)
+
+ if len(out)+m[0]-idx+len(exp) > tengo.MaxStringLen {
+ return "", false
+ }
+
+ out += src[idx:m[0]] + string(exp)
+ idx = m[1]
+ }
+ if idx < len(src) {
+ if len(out)+len(src)-idx > tengo.MaxStringLen {
+ return "", false
+ }
+
+ out += src[idx:]
+ }
+
+ return string(out), true
+}
diff --git a/vendor/github.com/d5/tengo/stdlib/times.go b/vendor/github.com/d5/tengo/stdlib/times.go
new file mode 100644
index 00000000..111c877a
--- /dev/null
+++ b/vendor/github.com/d5/tengo/stdlib/times.go
@@ -0,0 +1,989 @@
+package stdlib
+
+import (
+ "time"
+
+ "github.com/d5/tengo"
+ "github.com/d5/tengo/objects"
+)
+
+var timesModule = map[string]objects.Object{
+ "format_ansic": &objects.String{Value: time.ANSIC},
+ "format_unix_date": &objects.String{Value: time.UnixDate},
+ "format_ruby_date": &objects.String{Value: time.RubyDate},
+ "format_rfc822": &objects.String{Value: time.RFC822},
+ "format_rfc822z": &objects.String{Value: time.RFC822Z},
+ "format_rfc850": &objects.String{Value: time.RFC850},
+ "format_rfc1123": &objects.String{Value: time.RFC1123},
+ "format_rfc1123z": &objects.String{Value: time.RFC1123Z},
+ "format_rfc3339": &objects.String{Value: time.RFC3339},
+ "format_rfc3339_nano": &objects.String{Value: time.RFC3339Nano},
+ "format_kitchen": &objects.String{Value: time.Kitchen},
+ "format_stamp": &objects.String{Value: time.Stamp},
+ "format_stamp_milli": &objects.String{Value: time.StampMilli},
+ "format_stamp_micro": &objects.String{Value: time.StampMicro},
+ "format_stamp_nano": &objects.String{Value: time.StampNano},
+ "nanosecond": &objects.Int{Value: int64(time.Nanosecond)},
+ "microsecond": &objects.Int{Value: int64(time.Microsecond)},
+ "millisecond": &objects.Int{Value: int64(time.Millisecond)},
+ "second": &objects.Int{Value: int64(time.Second)},
+ "minute": &objects.Int{Value: int64(time.Minute)},
+ "hour": &objects.Int{Value: int64(time.Hour)},
+ "january": &objects.Int{Value: int64(time.January)},
+ "february": &objects.Int{Value: int64(time.February)},
+ "march": &objects.Int{Value: int64(time.March)},
+ "april": &objects.Int{Value: int64(time.April)},
+ "may": &objects.Int{Value: int64(time.May)},
+ "june": &objects.Int{Value: int64(time.June)},
+ "july": &objects.Int{Value: int64(time.July)},
+ "august": &objects.Int{Value: int64(time.August)},
+ "september": &objects.Int{Value: int64(time.September)},
+ "october": &objects.Int{Value: int64(time.October)},
+ "november": &objects.Int{Value: int64(time.November)},
+ "december": &objects.Int{Value: int64(time.December)},
+ "sleep": &objects.UserFunction{Name: "sleep", Value: timesSleep}, // sleep(int)
+ "parse_duration": &objects.UserFunction{Name: "parse_duration", Value: timesParseDuration}, // parse_duration(str) => int
+ "since": &objects.UserFunction{Name: "since", Value: timesSince}, // since(time) => int
+ "until": &objects.UserFunction{Name: "until", Value: timesUntil}, // until(time) => int
+ "duration_hours": &objects.UserFunction{Name: "duration_hours", Value: timesDurationHours}, // duration_hours(int) => float
+ "duration_minutes": &objects.UserFunction{Name: "duration_minutes", Value: timesDurationMinutes}, // duration_minutes(int) => float
+ "duration_nanoseconds": &objects.UserFunction{Name: "duration_nanoseconds", Value: timesDurationNanoseconds}, // duration_nanoseconds(int) => int
+ "duration_seconds": &objects.UserFunction{Name: "duration_seconds", Value: timesDurationSeconds}, // duration_seconds(int) => float
+ "duration_string": &objects.UserFunction{Name: "duration_string", Value: timesDurationString}, // duration_string(int) => string
+ "month_string": &objects.UserFunction{Name: "month_string", Value: timesMonthString}, // month_string(int) => string
+ "date": &objects.UserFunction{Name: "date", Value: timesDate}, // date(year, month, day, hour, min, sec, nsec) => time
+ "now": &objects.UserFunction{Name: "now", Value: timesNow}, // now() => time
+ "parse": &objects.UserFunction{Name: "parse", Value: timesParse}, // parse(format, str) => time
+ "unix": &objects.UserFunction{Name: "unix", Value: timesUnix}, // unix(sec, nsec) => time
+ "add": &objects.UserFunction{Name: "add", Value: timesAdd}, // add(time, int) => time
+ "add_date": &objects.UserFunction{Name: "add_date", Value: timesAddDate}, // add_date(time, years, months, days) => time
+ "sub": &objects.UserFunction{Name: "sub", Value: timesSub}, // sub(t time, u time) => int
+ "after": &objects.UserFunction{Name: "after", Value: timesAfter}, // after(t time, u time) => bool
+ "before": &objects.UserFunction{Name: "before", Value: timesBefore}, // before(t time, u time) => bool
+ "time_year": &objects.UserFunction{Name: "time_year", Value: timesTimeYear}, // time_year(time) => int
+ "time_month": &objects.UserFunction{Name: "time_month", Value: timesTimeMonth}, // time_month(time) => int
+ "time_day": &objects.UserFunction{Name: "time_day", Value: timesTimeDay}, // time_day(time) => int
+ "time_weekday": &objects.UserFunction{Name: "time_weekday", Value: timesTimeWeekday}, // time_weekday(time) => int
+ "time_hour": &objects.UserFunction{Name: "time_hour", Value: timesTimeHour}, // time_hour(time) => int
+ "time_minute": &objects.UserFunction{Name: "time_minute", Value: timesTimeMinute}, // time_minute(time) => int
+ "time_second": &objects.UserFunction{Name: "time_second", Value: timesTimeSecond}, // time_second(time) => int
+ "time_nanosecond": &objects.UserFunction{Name: "time_nanosecond", Value: timesTimeNanosecond}, // time_nanosecond(time) => int
+ "time_unix": &objects.UserFunction{Name: "time_unix", Value: timesTimeUnix}, // time_unix(time) => int
+ "time_unix_nano": &objects.UserFunction{Name: "time_unix_nano", Value: timesTimeUnixNano}, // time_unix_nano(time) => int
+ "time_format": &objects.UserFunction{Name: "time_format", Value: timesTimeFormat}, // time_format(time, format) => string
+ "time_location": &objects.UserFunction{Name: "time_location", Value: timesTimeLocation}, // time_location(time) => string
+ "time_string": &objects.UserFunction{Name: "time_string", Value: timesTimeString}, // time_string(time) => string
+ "is_zero": &objects.UserFunction{Name: "is_zero", Value: timesIsZero}, // is_zero(time) => bool
+ "to_local": &objects.UserFunction{Name: "to_local", Value: timesToLocal}, // to_local(time) => time
+ "to_utc": &objects.UserFunction{Name: "to_utc", Value: timesToUTC}, // to_utc(time) => time
+}
+
+func timesSleep(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ time.Sleep(time.Duration(i1))
+ ret = objects.UndefinedValue
+
+ return
+}
+
+func timesParseDuration(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ dur, err := time.ParseDuration(s1)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ ret = &objects.Int{Value: int64(dur)}
+
+ return
+}
+
+func timesSince(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(time.Since(t1))}
+
+ return
+}
+
+func timesUntil(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(time.Until(t1))}
+
+ return
+}
+
+func timesDurationHours(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Float{Value: time.Duration(i1).Hours()}
+
+ return
+}
+
+func timesDurationMinutes(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Float{Value: time.Duration(i1).Minutes()}
+
+ return
+}
+
+func timesDurationNanoseconds(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: time.Duration(i1).Nanoseconds()}
+
+ return
+}
+
+func timesDurationSeconds(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Float{Value: time.Duration(i1).Seconds()}
+
+ return
+}
+
+func timesDurationString(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.String{Value: time.Duration(i1).String()}
+
+ return
+}
+
+func timesMonthString(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.String{Value: time.Month(i1).String()}
+
+ return
+}
+
+func timesDate(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 7 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+ i3, ok := objects.ToInt(args[2])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+ i4, ok := objects.ToInt(args[3])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "fourth",
+ Expected: "int(compatible)",
+ Found: args[3].TypeName(),
+ }
+ return
+ }
+ i5, ok := objects.ToInt(args[4])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "fifth",
+ Expected: "int(compatible)",
+ Found: args[4].TypeName(),
+ }
+ return
+ }
+ i6, ok := objects.ToInt(args[5])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "sixth",
+ Expected: "int(compatible)",
+ Found: args[5].TypeName(),
+ }
+ return
+ }
+ i7, ok := objects.ToInt(args[6])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "seventh",
+ Expected: "int(compatible)",
+ Found: args[6].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Time{Value: time.Date(i1, time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location())}
+
+ return
+}
+
+func timesNow(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 0 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ ret = &objects.Time{Value: time.Now()}
+
+ return
+}
+
+func timesParse(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := objects.ToString(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ parsed, err := time.Parse(s1, s2)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ ret = &objects.Time{Value: parsed}
+
+ return
+}
+
+func timesUnix(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := objects.ToInt64(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := objects.ToInt64(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Time{Value: time.Unix(i1, i2)}
+
+ return
+}
+
+func timesAdd(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := objects.ToInt64(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Time{Value: t1.Add(time.Duration(i2))}
+
+ return
+}
+
+func timesSub(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ t2, ok := objects.ToTime(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "time(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Sub(t2))}
+
+ return
+}
+
+func timesAddDate(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 4 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := objects.ToInt(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ i3, ok := objects.ToInt(args[2])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ i4, ok := objects.ToInt(args[3])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "fourth",
+ Expected: "int(compatible)",
+ Found: args[3].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Time{Value: t1.AddDate(i2, i3, i4)}
+
+ return
+}
+
+func timesAfter(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ t2, ok := objects.ToTime(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "time(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ if t1.After(t2) {
+ ret = objects.TrueValue
+ } else {
+ ret = objects.FalseValue
+ }
+
+ return
+}
+
+func timesBefore(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ t2, ok := objects.ToTime(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ if t1.Before(t2) {
+ ret = objects.TrueValue
+ } else {
+ ret = objects.FalseValue
+ }
+
+ return
+}
+
+func timesTimeYear(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Year())}
+
+ return
+}
+
+func timesTimeMonth(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Month())}
+
+ return
+}
+
+func timesTimeDay(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Day())}
+
+ return
+}
+
+func timesTimeWeekday(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Weekday())}
+
+ return
+}
+
+func timesTimeHour(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Hour())}
+
+ return
+}
+
+func timesTimeMinute(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Minute())}
+
+ return
+}
+
+func timesTimeSecond(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Second())}
+
+ return
+}
+
+func timesTimeNanosecond(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Nanosecond())}
+
+ return
+}
+
+func timesTimeUnix(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.Unix())}
+
+ return
+}
+
+func timesTimeUnixNano(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Int{Value: int64(t1.UnixNano())}
+
+ return
+}
+
+func timesTimeFormat(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 2 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := objects.ToString(args[1])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ s := t1.Format(s2)
+ if len(s) > tengo.MaxStringLen {
+
+ return nil, objects.ErrStringLimit
+ }
+
+ ret = &objects.String{Value: s}
+
+ return
+}
+
+func timesIsZero(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ if t1.IsZero() {
+ ret = objects.TrueValue
+ } else {
+ ret = objects.FalseValue
+ }
+
+ return
+}
+
+func timesToLocal(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Time{Value: t1.Local()}
+
+ return
+}
+
+func timesToUTC(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.Time{Value: t1.UTC()}
+
+ return
+}
+
+func timesTimeLocation(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.String{Value: t1.Location().String()}
+
+ return
+}
+
+func timesTimeString(args ...objects.Object) (ret objects.Object, err error) {
+ if len(args) != 1 {
+ err = objects.ErrWrongNumArguments
+ return
+ }
+
+ t1, ok := objects.ToTime(args[0])
+ if !ok {
+ err = objects.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "time(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ ret = &objects.String{Value: t1.String()}
+
+ return
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index dfa18372..9cb748d2 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -17,14 +17,16 @@ github.com/Philipp15b/go-steam/rwu
github.com/Philipp15b/go-steam/socialcache
# github.com/bwmarrin/discordgo v0.19.0
github.com/bwmarrin/discordgo
-# github.com/d5/tengo v1.12.1
+# github.com/d5/tengo v1.20.0
github.com/d5/tengo/script
+github.com/d5/tengo/stdlib
github.com/d5/tengo/compiler
github.com/d5/tengo/compiler/parser
github.com/d5/tengo/compiler/source
github.com/d5/tengo/objects
github.com/d5/tengo/runtime
github.com/d5/tengo
+github.com/d5/tengo/stdlib/json
github.com/d5/tengo/compiler/ast
github.com/d5/tengo/compiler/token
github.com/d5/tengo/compiler/scanner