summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/objects
diff options
context:
space:
mode:
authorWim <wim@42.be>2019-02-23 16:39:44 +0100
committerGitHub <noreply@github.com>2019-02-23 16:39:44 +0100
commit1bb39eba8717f62336cc98c5bb7cfbef194f3626 (patch)
tree0437ae89473b8e25ad1c9597e1049a23a7933f6a /vendor/github.com/d5/tengo/objects
parent3190703dc8618896c932a23d8ca155fbbf6fab13 (diff)
downloadmatterbridge-msglm-1bb39eba8717f62336cc98c5bb7cfbef194f3626.tar.gz
matterbridge-msglm-1bb39eba8717f62336cc98c5bb7cfbef194f3626.tar.bz2
matterbridge-msglm-1bb39eba8717f62336cc98c5bb7cfbef194f3626.zip
Add scripting (tengo) support for every incoming message (#731)
TengoModifyMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script. This script will receive every incoming message and can be used to modify the Username and the Text of that message. The script will have the following global variables: to modify: msgUsername and msgText to read: msgChannel and msgAccount The script is reloaded on every message, so you can modify the script on the fly. Example script can be found in https://github.com/42wim/matterbridge/tree/master/gateway/bench.tengo and https://github.com/42wim/matterbridge/tree/master/contrib/example.tengo The example below will check if the text contains blah and if so, it'll replace the text and the username of that message. text := import("text") if text.re_match("blah",msgText) { msgText="replaced by this" msgUsername="fakeuser" } More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and https://github.com/d5/tengo/blob/master/docs/stdlib.md
Diffstat (limited to 'vendor/github.com/d5/tengo/objects')
-rw-r--r--vendor/github.com/d5/tengo/objects/array.go130
-rw-r--r--vendor/github.com/d5/tengo/objects/array_iterator.go57
-rw-r--r--vendor/github.com/d5/tengo/objects/bool.go64
-rw-r--r--vendor/github.com/d5/tengo/objects/break.go37
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_append.go21
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_convert.go155
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_copy.go9
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_function.go47
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_json.go54
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_len.go29
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_print.go75
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_type.go9
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_type_checks.go183
-rw-r--r--vendor/github.com/d5/tengo/objects/builtins.go135
-rw-r--r--vendor/github.com/d5/tengo/objects/bytes.go76
-rw-r--r--vendor/github.com/d5/tengo/objects/callable.go9
-rw-r--r--vendor/github.com/d5/tengo/objects/callable_func.go4
-rw-r--r--vendor/github.com/d5/tengo/objects/char.go119
-rw-r--r--vendor/github.com/d5/tengo/objects/closure.go45
-rw-r--r--vendor/github.com/d5/tengo/objects/compiled_function.go49
-rw-r--r--vendor/github.com/d5/tengo/objects/continue.go38
-rw-r--r--vendor/github.com/d5/tengo/objects/conversion.go249
-rw-r--r--vendor/github.com/d5/tengo/objects/error.go47
-rw-r--r--vendor/github.com/d5/tengo/objects/errors.go32
-rw-r--r--vendor/github.com/d5/tengo/objects/float.go146
-rw-r--r--vendor/github.com/d5/tengo/objects/immautable_array.go109
-rw-r--r--vendor/github.com/d5/tengo/objects/immutable_map.go105
-rw-r--r--vendor/github.com/d5/tengo/objects/index_assignable.go9
-rw-r--r--vendor/github.com/d5/tengo/objects/indexable.go9
-rw-r--r--vendor/github.com/d5/tengo/objects/int.go198
-rw-r--r--vendor/github.com/d5/tengo/objects/iterable.go7
-rw-r--r--vendor/github.com/d5/tengo/objects/iterator.go15
-rw-r--r--vendor/github.com/d5/tengo/objects/map.go118
-rw-r--r--vendor/github.com/d5/tengo/objects/map_iterator.go62
-rw-r--r--vendor/github.com/d5/tengo/objects/object.go30
-rw-r--r--vendor/github.com/d5/tengo/objects/objects.go12
-rw-r--r--vendor/github.com/d5/tengo/objects/return_value.go39
-rw-r--r--vendor/github.com/d5/tengo/objects/string.go95
-rw-r--r--vendor/github.com/d5/tengo/objects/string_iterator.go57
-rw-r--r--vendor/github.com/d5/tengo/objects/time.go89
-rw-r--r--vendor/github.com/d5/tengo/objects/undefined.go42
-rw-r--r--vendor/github.com/d5/tengo/objects/user_function.go47
42 files changed, 2862 insertions, 0 deletions
diff --git a/vendor/github.com/d5/tengo/objects/array.go b/vendor/github.com/d5/tengo/objects/array.go
new file mode 100644
index 00000000..1e917c59
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/array.go
@@ -0,0 +1,130 @@
+package objects
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Array represents an array of objects.
+type Array struct {
+ Value []Object
+}
+
+// TypeName returns the name of the type.
+func (o *Array) TypeName() string {
+ return "array"
+}
+
+func (o *Array) String() string {
+ var elements []string
+ for _, e := range o.Value {
+ elements = append(elements, e.String())
+ }
+
+ return fmt.Sprintf("[%s]", strings.Join(elements, ", "))
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ if rhs, ok := rhs.(*Array); ok {
+ switch op {
+ case token.Add:
+ if len(rhs.Value) == 0 {
+ return o, nil
+ }
+ return &Array{Value: append(o.Value, rhs.Value...)}, nil
+ }
+ }
+
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Array) Copy() Object {
+ var c []Object
+ for _, elem := range o.Value {
+ c = append(c, elem.Copy())
+ }
+
+ return &Array{Value: c}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Array) IsFalsy() bool {
+ return len(o.Value) == 0
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Array) Equals(x Object) bool {
+ var xVal []Object
+ switch x := x.(type) {
+ case *Array:
+ xVal = x.Value
+ case *ImmutableArray:
+ xVal = x.Value
+ default:
+ return false
+ }
+
+ if len(o.Value) != len(xVal) {
+ return false
+ }
+
+ for i, e := range o.Value {
+ if !e.Equals(xVal[i]) {
+ return false
+ }
+ }
+
+ return true
+}
+
+// IndexGet returns an element at a given index.
+func (o *Array) IndexGet(index Object) (res Object, err error) {
+ intIdx, ok := index.(*Int)
+ if !ok {
+ err = ErrInvalidIndexType
+ return
+ }
+
+ idxVal := int(intIdx.Value)
+
+ if idxVal < 0 || idxVal >= len(o.Value) {
+ res = UndefinedValue
+ return
+ }
+
+ res = o.Value[idxVal]
+
+ return
+}
+
+// IndexSet sets an element at a given index.
+func (o *Array) IndexSet(index, value Object) (err error) {
+ intIdx, ok := ToInt(index)
+ if !ok {
+ err = ErrInvalidIndexType
+ return
+ }
+
+ if intIdx < 0 || intIdx >= len(o.Value) {
+ err = ErrIndexOutOfBounds
+ return
+ }
+
+ o.Value[intIdx] = value
+
+ return nil
+}
+
+// Iterate creates an array iterator.
+func (o *Array) Iterate() Iterator {
+ return &ArrayIterator{
+ v: o.Value,
+ l: len(o.Value),
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/array_iterator.go b/vendor/github.com/d5/tengo/objects/array_iterator.go
new file mode 100644
index 00000000..204faa41
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/array_iterator.go
@@ -0,0 +1,57 @@
+package objects
+
+import "github.com/d5/tengo/compiler/token"
+
+// ArrayIterator is an iterator for an array.
+type ArrayIterator struct {
+ v []Object
+ i int
+ l int
+}
+
+// TypeName returns the name of the type.
+func (i *ArrayIterator) TypeName() string {
+ return "array-iterator"
+}
+
+func (i *ArrayIterator) String() string {
+ return "<array-iterator>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (i *ArrayIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (i *ArrayIterator) IsFalsy() bool {
+ return true
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (i *ArrayIterator) Equals(Object) bool {
+ return false
+}
+
+// Copy returns a copy of the type.
+func (i *ArrayIterator) Copy() Object {
+ return &ArrayIterator{v: i.v, i: i.i, l: i.l}
+}
+
+// Next returns true if there are more elements to iterate.
+func (i *ArrayIterator) Next() bool {
+ i.i++
+ return i.i <= i.l
+}
+
+// Key returns the key or index value of the current element.
+func (i *ArrayIterator) Key() Object {
+ return &Int{Value: int64(i.i - 1)}
+}
+
+// Value returns the value of the current element.
+func (i *ArrayIterator) Value() Object {
+ return i.v[i.i-1]
+}
diff --git a/vendor/github.com/d5/tengo/objects/bool.go b/vendor/github.com/d5/tengo/objects/bool.go
new file mode 100644
index 00000000..ac9949e4
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/bool.go
@@ -0,0 +1,64 @@
+package objects
+
+import (
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Bool represents a boolean value.
+type Bool struct {
+ // this is intentionally non-public to force using objects.TrueValue and FalseValue always
+ value bool
+}
+
+func (o *Bool) String() string {
+ if o.value {
+ return "true"
+ }
+
+ return "false"
+}
+
+// TypeName returns the name of the type.
+func (o *Bool) TypeName() string {
+ return "bool"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Bool) Copy() Object {
+ return o
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Bool) IsFalsy() bool {
+ return !o.value
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Bool) Equals(x Object) bool {
+ return o == x
+}
+
+// GobDecode decodes bool value from input bytes.
+func (o *Bool) GobDecode(b []byte) (err error) {
+ o.value = b[0] == 1
+
+ return
+}
+
+// GobEncode encodes bool values into bytes.
+func (o *Bool) GobEncode() (b []byte, err error) {
+ if o.value {
+ b = []byte{1}
+ } else {
+ b = []byte{0}
+ }
+
+ return
+}
diff --git a/vendor/github.com/d5/tengo/objects/break.go b/vendor/github.com/d5/tengo/objects/break.go
new file mode 100644
index 00000000..cd473a87
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/break.go
@@ -0,0 +1,37 @@
+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_append.go b/vendor/github.com/d5/tengo/objects/builtin_append.go
new file mode 100644
index 00000000..9fb14b82
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_append.go
@@ -0,0 +1,21 @@
+package objects
+
+// append(arr, items...)
+func builtinAppend(args ...Object) (Object, error) {
+ if len(args) < 2 {
+ return nil, ErrWrongNumArguments
+ }
+
+ switch arg := args[0].(type) {
+ case *Array:
+ return &Array{Value: append(arg.Value, args[1:]...)}, nil
+ case *ImmutableArray:
+ return &Array{Value: append(arg.Value, args[1:]...)}, nil
+ default:
+ return nil, ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "array",
+ Found: arg.TypeName(),
+ }
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_convert.go b/vendor/github.com/d5/tengo/objects/builtin_convert.go
new file mode 100644
index 00000000..7d9a8733
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_convert.go
@@ -0,0 +1,155 @@
+package objects
+
+func builtinString(args ...Object) (Object, error) {
+ argsLen := len(args)
+ if !(argsLen == 1 || argsLen == 2) {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*String); ok {
+ return args[0], nil
+ }
+
+ v, ok := ToString(args[0])
+ if ok {
+ return &String{Value: v}, nil
+ }
+
+ if argsLen == 2 {
+ return args[1], nil
+ }
+
+ return UndefinedValue, nil
+}
+
+func builtinInt(args ...Object) (Object, error) {
+ argsLen := len(args)
+ if !(argsLen == 1 || argsLen == 2) {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Int); ok {
+ return args[0], nil
+ }
+
+ v, ok := ToInt64(args[0])
+ if ok {
+ return &Int{Value: v}, nil
+ }
+
+ if argsLen == 2 {
+ return args[1], nil
+ }
+
+ return UndefinedValue, nil
+}
+
+func builtinFloat(args ...Object) (Object, error) {
+ argsLen := len(args)
+ if !(argsLen == 1 || argsLen == 2) {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Float); ok {
+ return args[0], nil
+ }
+
+ v, ok := ToFloat64(args[0])
+ if ok {
+ return &Float{Value: v}, nil
+ }
+
+ if argsLen == 2 {
+ return args[1], nil
+ }
+
+ return UndefinedValue, nil
+}
+
+func builtinBool(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Bool); ok {
+ return args[0], nil
+ }
+
+ v, ok := ToBool(args[0])
+ if ok {
+ if v {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+ }
+
+ return UndefinedValue, nil
+}
+
+func builtinChar(args ...Object) (Object, error) {
+ argsLen := len(args)
+ if !(argsLen == 1 || argsLen == 2) {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Char); ok {
+ return args[0], nil
+ }
+
+ v, ok := ToRune(args[0])
+ if ok {
+ return &Char{Value: v}, nil
+ }
+
+ if argsLen == 2 {
+ return args[1], nil
+ }
+
+ return UndefinedValue, nil
+}
+
+func builtinBytes(args ...Object) (Object, error) {
+ argsLen := len(args)
+ if !(argsLen == 1 || argsLen == 2) {
+ return nil, ErrWrongNumArguments
+ }
+
+ // bytes(N) => create a new bytes with given size N
+ if n, ok := args[0].(*Int); ok {
+ return &Bytes{Value: make([]byte, int(n.Value))}, nil
+ }
+
+ v, ok := ToByteSlice(args[0])
+ if ok {
+ return &Bytes{Value: v}, nil
+ }
+
+ if argsLen == 2 {
+ return args[1], nil
+ }
+
+ return UndefinedValue, nil
+}
+
+func builtinTime(args ...Object) (Object, error) {
+ argsLen := len(args)
+ if !(argsLen == 1 || argsLen == 2) {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Time); ok {
+ return args[0], nil
+ }
+
+ v, ok := ToTime(args[0])
+ if ok {
+ return &Time{Value: v}, nil
+ }
+
+ if argsLen == 2 {
+ return args[1], nil
+ }
+
+ return UndefinedValue, nil
+}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_copy.go b/vendor/github.com/d5/tengo/objects/builtin_copy.go
new file mode 100644
index 00000000..4b254b2b
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_copy.go
@@ -0,0 +1,9 @@
+package objects
+
+func builtinCopy(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ return args[0].Copy(), nil
+}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_function.go b/vendor/github.com/d5/tengo/objects/builtin_function.go
new file mode 100644
index 00000000..1d021617
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_function.go
@@ -0,0 +1,47 @@
+package objects
+
+import (
+ "github.com/d5/tengo/compiler/token"
+)
+
+// BuiltinFunction represents a builtin function.
+type BuiltinFunction struct {
+ Name string
+ Value CallableFunc
+}
+
+// TypeName returns the name of the type.
+func (o *BuiltinFunction) TypeName() string {
+ return "builtin-function:" + o.Name
+}
+
+func (o *BuiltinFunction) String() string {
+ return "<builtin-function>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *BuiltinFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *BuiltinFunction) Copy() Object {
+ return &BuiltinFunction{Value: o.Value}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *BuiltinFunction) IsFalsy() bool {
+ return false
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *BuiltinFunction) Equals(x Object) bool {
+ return false
+}
+
+// Call executes a builtin function.
+func (o *BuiltinFunction) Call(args ...Object) (Object, error) {
+ return o.Value(args...)
+}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_json.go b/vendor/github.com/d5/tengo/objects/builtin_json.go
new file mode 100644
index 00000000..c0810f7d
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_json.go
@@ -0,0 +1,54 @@
+package objects
+
+import (
+ "encoding/json"
+)
+
+// 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
+ }
+
+ 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_len.go b/vendor/github.com/d5/tengo/objects/builtin_len.go
new file mode 100644
index 00000000..39fbedd8
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_len.go
@@ -0,0 +1,29 @@
+package objects
+
+// len(obj object) => int
+func builtinLen(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ switch arg := args[0].(type) {
+ case *Array:
+ return &Int{Value: int64(len(arg.Value))}, nil
+ case *ImmutableArray:
+ return &Int{Value: int64(len(arg.Value))}, nil
+ case *String:
+ return &Int{Value: int64(len(arg.Value))}, nil
+ case *Bytes:
+ return &Int{Value: int64(len(arg.Value))}, nil
+ case *Map:
+ return &Int{Value: int64(len(arg.Value))}, nil
+ case *ImmutableMap:
+ return &Int{Value: int64(len(arg.Value))}, nil
+ default:
+ return nil, ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "array/string/bytes/map",
+ Found: arg.TypeName(),
+ }
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_print.go b/vendor/github.com/d5/tengo/objects/builtin_print.go
new file mode 100644
index 00000000..c5fe36db
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_print.go
@@ -0,0 +1,75 @@
+package objects
+
+import (
+ "fmt"
+)
+
+// 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)
+ }
+
+ return &String{Value: fmt.Sprintf(format.Value, formatArgs...)}, nil
+}
diff --git a/vendor/github.com/d5/tengo/objects/builtin_type.go b/vendor/github.com/d5/tengo/objects/builtin_type.go
new file mode 100644
index 00000000..376c26bb
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_type.go
@@ -0,0 +1,9 @@
+package objects
+
+func builtinTypeName(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ return &String{Value: args[0].TypeName()}, 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
new file mode 100644
index 00000000..960f7828
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_type_checks.go
@@ -0,0 +1,183 @@
+package objects
+
+func builtinIsString(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*String); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsInt(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Int); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsFloat(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Float); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsBool(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Bool); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsChar(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Char); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsBytes(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Bytes); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsArray(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Array); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsImmutableArray(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*ImmutableArray); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsMap(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Map); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsImmutableMap(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*ImmutableMap); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsTime(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Time); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsError(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if _, ok := args[0].(*Error); ok {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsUndefined(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ if args[0] == UndefinedValue {
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsFunction(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ switch args[0].(type) {
+ case *CompiledFunction, *Closure:
+ return TrueValue, nil
+ }
+
+ return FalseValue, nil
+}
+
+func builtinIsCallable(args ...Object) (Object, error) {
+ if len(args) != 1 {
+ return nil, ErrWrongNumArguments
+ }
+
+ switch args[0].(type) {
+ case *CompiledFunction, *Closure, Callable: // BuiltinFunction is Callable
+ 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
new file mode 100644
index 00000000..67553932
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtins.go
@@ -0,0 +1,135 @@
+package objects
+
+// NamedBuiltinFunc is a named builtin function.
+type NamedBuiltinFunc struct {
+ Name string
+ Func CallableFunc
+}
+
+// Builtins contains all default builtin functions.
+var Builtins = []NamedBuiltinFunc{
+ {
+ Name: "print",
+ Func: builtinPrint,
+ },
+ {
+ Name: "printf",
+ Func: builtinPrintf,
+ },
+ {
+ Name: "sprintf",
+ Func: builtinSprintf,
+ },
+ {
+ Name: "len",
+ Func: builtinLen,
+ },
+ {
+ Name: "copy",
+ Func: builtinCopy,
+ },
+ {
+ Name: "append",
+ Func: builtinAppend,
+ },
+ {
+ Name: "string",
+ Func: builtinString,
+ },
+ {
+ Name: "int",
+ Func: builtinInt,
+ },
+ {
+ Name: "bool",
+ Func: builtinBool,
+ },
+ {
+ Name: "float",
+ Func: builtinFloat,
+ },
+ {
+ Name: "char",
+ Func: builtinChar,
+ },
+ {
+ Name: "bytes",
+ Func: builtinBytes,
+ },
+ {
+ Name: "time",
+ Func: builtinTime,
+ },
+ {
+ Name: "is_int",
+ Func: builtinIsInt,
+ },
+ {
+ Name: "is_float",
+ Func: builtinIsFloat,
+ },
+ {
+ Name: "is_string",
+ Func: builtinIsString,
+ },
+ {
+ Name: "is_bool",
+ Func: builtinIsBool,
+ },
+ {
+ Name: "is_char",
+ Func: builtinIsChar,
+ },
+ {
+ Name: "is_bytes",
+ Func: builtinIsBytes,
+ },
+ {
+ Name: "is_array",
+ Func: builtinIsArray,
+ },
+ {
+ Name: "is_immutable_array",
+ Func: builtinIsImmutableArray,
+ },
+ {
+ Name: "is_map",
+ Func: builtinIsMap,
+ },
+ {
+ Name: "is_immutable_map",
+ Func: builtinIsImmutableMap,
+ },
+ {
+ Name: "is_time",
+ Func: builtinIsTime,
+ },
+ {
+ Name: "is_error",
+ Func: builtinIsError,
+ },
+ {
+ Name: "is_undefined",
+ Func: builtinIsUndefined,
+ },
+ {
+ Name: "is_function",
+ Func: builtinIsFunction,
+ },
+ {
+ Name: "is_callable",
+ Func: builtinIsCallable,
+ },
+ {
+ Name: "to_json",
+ Func: builtinToJSON,
+ },
+ {
+ Name: "from_json",
+ Func: builtinFromJSON,
+ },
+ {
+ Name: "type_name",
+ Func: builtinTypeName,
+ },
+}
diff --git a/vendor/github.com/d5/tengo/objects/bytes.go b/vendor/github.com/d5/tengo/objects/bytes.go
new file mode 100644
index 00000000..7d8d6694
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/bytes.go
@@ -0,0 +1,76 @@
+package objects
+
+import (
+ "bytes"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Bytes represents a byte array.
+type Bytes struct {
+ Value []byte
+}
+
+func (o *Bytes) String() string {
+ return string(o.Value)
+}
+
+// TypeName returns the name of the type.
+func (o *Bytes) TypeName() string {
+ return "bytes"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Bytes) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ switch op {
+ case token.Add:
+ switch rhs := rhs.(type) {
+ case *Bytes:
+ return &Bytes{Value: append(o.Value, rhs.Value...)}, nil
+ }
+ }
+
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Bytes) Copy() Object {
+ return &Bytes{Value: append([]byte{}, o.Value...)}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Bytes) IsFalsy() bool {
+ return len(o.Value) == 0
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Bytes) Equals(x Object) bool {
+ t, ok := x.(*Bytes)
+ if !ok {
+ return false
+ }
+
+ return bytes.Compare(o.Value, t.Value) == 0
+}
+
+// IndexGet returns an element (as Int) at a given index.
+func (o *Bytes) IndexGet(index Object) (res Object, err error) {
+ intIdx, ok := index.(*Int)
+ if !ok {
+ err = ErrInvalidIndexType
+ return
+ }
+
+ idxVal := int(intIdx.Value)
+
+ if idxVal < 0 || idxVal >= len(o.Value) {
+ res = UndefinedValue
+ return
+ }
+
+ res = &Int{Value: int64(o.Value[idxVal])}
+
+ return
+}
diff --git a/vendor/github.com/d5/tengo/objects/callable.go b/vendor/github.com/d5/tengo/objects/callable.go
new file mode 100644
index 00000000..a066e1b9
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/callable.go
@@ -0,0 +1,9 @@
+package objects
+
+// Callable represents an object that can be called like a function.
+type Callable interface {
+ // Call should take an arbitrary number of arguments
+ // and returns a return value and/or an error,
+ // which the VM will consider as a run-time error.
+ Call(args ...Object) (ret Object, err error)
+}
diff --git a/vendor/github.com/d5/tengo/objects/callable_func.go b/vendor/github.com/d5/tengo/objects/callable_func.go
new file mode 100644
index 00000000..cf9b43aa
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/callable_func.go
@@ -0,0 +1,4 @@
+package objects
+
+// CallableFunc is a function signature for the callable functions.
+type CallableFunc func(args ...Object) (ret Object, err error)
diff --git a/vendor/github.com/d5/tengo/objects/char.go b/vendor/github.com/d5/tengo/objects/char.go
new file mode 100644
index 00000000..4458bd12
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/char.go
@@ -0,0 +1,119 @@
+package objects
+
+import (
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Char represents a character value.
+type Char struct {
+ Value rune
+}
+
+func (o *Char) String() string {
+ return string(o.Value)
+}
+
+// TypeName returns the name of the type.
+func (o *Char) TypeName() string {
+ return "char"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Char) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ switch rhs := rhs.(type) {
+ case *Char:
+ switch op {
+ case token.Add:
+ r := o.Value + rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Char{Value: r}, nil
+ case token.Sub:
+ r := o.Value - rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Char{Value: r}, nil
+ case token.Less:
+ if o.Value < rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.Greater:
+ if o.Value > rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.LessEq:
+ if o.Value <= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.GreaterEq:
+ if o.Value >= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ }
+ case *Int:
+ switch op {
+ case token.Add:
+ r := o.Value + rune(rhs.Value)
+ if r == o.Value {
+ return o, nil
+ }
+ return &Char{Value: r}, nil
+ case token.Sub:
+ r := o.Value - rune(rhs.Value)
+ if r == o.Value {
+ return o, nil
+ }
+ return &Char{Value: r}, nil
+ case token.Less:
+ if int64(o.Value) < rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.Greater:
+ if int64(o.Value) > rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.LessEq:
+ if int64(o.Value) <= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.GreaterEq:
+ if int64(o.Value) >= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ }
+ }
+
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Char) Copy() Object {
+ return &Char{Value: o.Value}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Char) IsFalsy() bool {
+ return o.Value == 0
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Char) Equals(x Object) bool {
+ t, ok := x.(*Char)
+ if !ok {
+ return false
+ }
+
+ return o.Value == t.Value
+}
diff --git a/vendor/github.com/d5/tengo/objects/closure.go b/vendor/github.com/d5/tengo/objects/closure.go
new file mode 100644
index 00000000..d4915a52
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/closure.go
@@ -0,0 +1,45 @@
+package objects
+
+import (
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Closure represents a function closure.
+type Closure struct {
+ Fn *CompiledFunction
+ Free []*Object
+}
+
+// TypeName returns the name of the type.
+func (o *Closure) TypeName() string {
+ return "closure"
+}
+
+func (o *Closure) String() string {
+ return "<closure>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+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
+ }
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Closure) IsFalsy() bool {
+ return false
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Closure) Equals(x Object) bool {
+ return false
+}
diff --git a/vendor/github.com/d5/tengo/objects/compiled_function.go b/vendor/github.com/d5/tengo/objects/compiled_function.go
new file mode 100644
index 00000000..d20f2375
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/compiled_function.go
@@ -0,0 +1,49 @@
+package objects
+
+import (
+ "github.com/d5/tengo/compiler/source"
+ "github.com/d5/tengo/compiler/token"
+)
+
+// CompiledFunction represents a compiled function.
+type CompiledFunction struct {
+ Instructions []byte
+ NumLocals int // number of local variables (including function parameters)
+ NumParameters int
+ SourceMap map[int]source.Pos
+}
+
+// TypeName returns the name of the type.
+func (o *CompiledFunction) TypeName() string {
+ return "compiled-function"
+}
+
+func (o *CompiledFunction) String() string {
+ return "<compiled-function>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *CompiledFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *CompiledFunction) Copy() Object {
+ return &CompiledFunction{
+ Instructions: append([]byte{}, o.Instructions...),
+ NumLocals: o.NumLocals,
+ NumParameters: o.NumParameters,
+ }
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *CompiledFunction) IsFalsy() bool {
+ return false
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *CompiledFunction) Equals(x Object) bool {
+ return false
+}
diff --git a/vendor/github.com/d5/tengo/objects/continue.go b/vendor/github.com/d5/tengo/objects/continue.go
new file mode 100644
index 00000000..8094e686
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/continue.go
@@ -0,0 +1,38 @@
+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
new file mode 100644
index 00000000..3c17546f
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/conversion.go
@@ -0,0 +1,249 @@
+package objects
+
+import (
+ "fmt"
+ "strconv"
+ "time"
+)
+
+// ToString will try to convert object o to string value.
+func ToString(o Object) (v string, ok bool) {
+ if o == UndefinedValue {
+ //ok = false
+ return
+ }
+
+ ok = true
+
+ if str, isStr := o.(*String); isStr {
+ v = str.Value
+ } else {
+ v = o.String()
+ }
+
+ return
+}
+
+// ToInt will try to convert object o to int value.
+func ToInt(o Object) (v int, ok bool) {
+ switch o := o.(type) {
+ case *Int:
+ v = int(o.Value)
+ ok = true
+ case *Float:
+ v = int(o.Value)
+ ok = true
+ case *Char:
+ v = int(o.Value)
+ ok = true
+ case *Bool:
+ if o == TrueValue {
+ v = 1
+ }
+ ok = true
+ case *String:
+ c, err := strconv.ParseInt(o.Value, 10, 64)
+ if err == nil {
+ v = int(c)
+ ok = true
+ }
+ }
+
+ //ok = false
+ return
+}
+
+// ToInt64 will try to convert object o to int64 value.
+func ToInt64(o Object) (v int64, ok bool) {
+ switch o := o.(type) {
+ case *Int:
+ v = o.Value
+ ok = true
+ case *Float:
+ v = int64(o.Value)
+ ok = true
+ case *Char:
+ v = int64(o.Value)
+ ok = true
+ case *Bool:
+ if o == TrueValue {
+ v = 1
+ }
+ ok = true
+ case *String:
+ c, err := strconv.ParseInt(o.Value, 10, 64)
+ if err == nil {
+ v = c
+ ok = true
+ }
+ }
+
+ //ok = false
+ return
+}
+
+// ToFloat64 will try to convert object o to float64 value.
+func ToFloat64(o Object) (v float64, ok bool) {
+ switch o := o.(type) {
+ case *Int:
+ v = float64(o.Value)
+ ok = true
+ case *Float:
+ v = o.Value
+ ok = true
+ case *String:
+ c, err := strconv.ParseFloat(o.Value, 64)
+ if err == nil {
+ v = c
+ ok = true
+ }
+ }
+
+ //ok = false
+ return
+}
+
+// ToBool will try to convert object o to bool value.
+func ToBool(o Object) (v bool, ok bool) {
+ ok = true
+ v = !o.IsFalsy()
+
+ return
+}
+
+// ToRune will try to convert object o to rune value.
+func ToRune(o Object) (v rune, ok bool) {
+ switch o := o.(type) {
+ case *Int:
+ v = rune(o.Value)
+ ok = true
+ case *Char:
+ v = rune(o.Value)
+ ok = true
+ }
+
+ //ok = false
+ return
+}
+
+// ToByteSlice will try to convert object o to []byte value.
+func ToByteSlice(o Object) (v []byte, ok bool) {
+ switch o := o.(type) {
+ case *Bytes:
+ v = o.Value
+ ok = true
+ case *String:
+ v = []byte(o.Value)
+ ok = true
+ }
+
+ //ok = false
+ return
+}
+
+// ToTime will try to convert object o to time.Time value.
+func ToTime(o Object) (v time.Time, ok bool) {
+ switch o := o.(type) {
+ case *Time:
+ v = o.Value
+ ok = true
+ case *Int:
+ v = time.Unix(o.Value, 0)
+ ok = true
+ }
+
+ //ok = false
+ return
+}
+
+// objectToInterface attempts to convert an object o to an interface{} value
+func objectToInterface(o Object) (res interface{}) {
+ switch o := o.(type) {
+ case *Int:
+ res = o.Value
+ case *String:
+ res = o.Value
+ case *Float:
+ res = o.Value
+ case *Bool:
+ res = o == TrueValue
+ case *Char:
+ res = o.Value
+ case *Bytes:
+ res = o.Value
+ case *Array:
+ res = make([]interface{}, len(o.Value))
+ for i, val := range o.Value {
+ res.([]interface{})[i] = objectToInterface(val)
+ }
+ case *Map:
+ res = make(map[string]interface{})
+ for key, v := range o.Value {
+ res.(map[string]interface{})[key] = objectToInterface(v)
+ }
+ case Object:
+ return o
+ }
+
+ return
+}
+
+// FromInterface will attempt to convert an interface{} v to a Tengo Object
+func FromInterface(v interface{}) (Object, error) {
+ switch v := v.(type) {
+ case nil:
+ return UndefinedValue, nil
+ case string:
+ return &String{Value: v}, nil
+ case int64:
+ return &Int{Value: v}, nil
+ case int:
+ return &Int{Value: int64(v)}, nil
+ case bool:
+ if v {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case rune:
+ return &Char{Value: v}, nil
+ case byte:
+ return &Char{Value: rune(v)}, nil
+ case float64:
+ return &Float{Value: v}, nil
+ case []byte:
+ return &Bytes{Value: v}, nil
+ case error:
+ return &Error{Value: &String{Value: v.Error()}}, nil
+ case map[string]Object:
+ return &Map{Value: v}, nil
+ case map[string]interface{}:
+ kv := make(map[string]Object)
+ for vk, vv := range v {
+ vo, err := FromInterface(vv)
+ if err != nil {
+ return nil, err
+ }
+ kv[vk] = vo
+ }
+ return &Map{Value: kv}, nil
+ case []Object:
+ return &Array{Value: v}, nil
+ case []interface{}:
+ arr := make([]Object, len(v), len(v))
+ for i, e := range v {
+ vo, err := FromInterface(e)
+ if err != nil {
+ return nil, err
+ }
+
+ arr[i] = vo
+ }
+ return &Array{Value: arr}, nil
+ case time.Time:
+ return &Time{Value: v}, nil
+ case Object:
+ return v, nil
+ }
+
+ return nil, fmt.Errorf("cannot convert to object: %T", v)
+}
diff --git a/vendor/github.com/d5/tengo/objects/error.go b/vendor/github.com/d5/tengo/objects/error.go
new file mode 100644
index 00000000..be21de03
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/error.go
@@ -0,0 +1,47 @@
+package objects
+
+import (
+ "fmt"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Error represents a string value.
+type Error struct {
+ Value Object
+}
+
+// TypeName returns the name of the type.
+func (o *Error) TypeName() string {
+ return "error"
+}
+
+func (o *Error) String() string {
+ if o.Value != nil {
+ return fmt.Sprintf("error: %s", o.Value.String())
+ }
+
+ return "error"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Error) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Error) IsFalsy() bool {
+ return true // error is always false.
+}
+
+// Copy returns a copy of the type.
+func (o *Error) Copy() Object {
+ return &Error{Value: o.Value.Copy()}
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Error) Equals(x Object) bool {
+ return o == x // pointer equality
+}
diff --git a/vendor/github.com/d5/tengo/objects/errors.go b/vendor/github.com/d5/tengo/objects/errors.go
new file mode 100644
index 00000000..e4012314
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/errors.go
@@ -0,0 +1,32 @@
+package objects
+
+import (
+ "errors"
+ "fmt"
+)
+
+// ErrIndexOutOfBounds is an error where a given index is out of the bounds.
+var ErrIndexOutOfBounds = errors.New("index out of bounds")
+
+// ErrInvalidIndexType represents an invalid index type.
+var ErrInvalidIndexType = errors.New("invalid index type")
+
+// ErrInvalidIndexValueType represents an invalid index value type.
+var ErrInvalidIndexValueType = errors.New("invalid index value type")
+
+// ErrInvalidOperator represents an error for invalid operator usage.
+var ErrInvalidOperator = errors.New("invalid operator")
+
+// ErrWrongNumArguments represents a wrong number of arguments error.
+var ErrWrongNumArguments = errors.New("wrong number of arguments")
+
+// ErrInvalidArgumentType represents an invalid argument value type error.
+type ErrInvalidArgumentType struct {
+ Name string
+ Expected string
+ Found string
+}
+
+func (e ErrInvalidArgumentType) Error() string {
+ return fmt.Sprintf("invalid type for argument '%s': expected %s, found %s", e.Name, e.Expected, e.Found)
+}
diff --git a/vendor/github.com/d5/tengo/objects/float.go b/vendor/github.com/d5/tengo/objects/float.go
new file mode 100644
index 00000000..65997303
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/float.go
@@ -0,0 +1,146 @@
+package objects
+
+import (
+ "math"
+ "strconv"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Float represents a floating point number value.
+type Float struct {
+ Value float64
+}
+
+func (o *Float) String() string {
+ return strconv.FormatFloat(o.Value, 'f', -1, 64)
+}
+
+// TypeName returns the name of the type.
+func (o *Float) TypeName() string {
+ return "float"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ switch rhs := rhs.(type) {
+ case *Float:
+ switch op {
+ case token.Add:
+ r := o.Value + rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Float{Value: r}, nil
+ case token.Sub:
+ r := o.Value - rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Float{Value: r}, nil
+ case token.Mul:
+ r := o.Value * rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Float{Value: r}, nil
+ case token.Quo:
+ r := o.Value / rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Float{Value: r}, nil
+ case token.Less:
+ if o.Value < rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.Greater:
+ if o.Value > rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.LessEq:
+ if o.Value <= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.GreaterEq:
+ if o.Value >= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ }
+ case *Int:
+ switch op {
+ case token.Add:
+ r := o.Value + float64(rhs.Value)
+ if r == o.Value {
+ return o, nil
+ }
+ return &Float{Value: r}, nil
+ case token.Sub:
+ r := o.Value - float64(rhs.Value)
+ if r == o.Value {
+ return o, nil
+ }
+ return &Float{Value: r}, nil
+ case token.Mul:
+ r := o.Value * float64(rhs.Value)
+ if r == o.Value {
+ return o, nil
+ }
+ return &Float{Value: r}, nil
+ case token.Quo:
+ r := o.Value / float64(rhs.Value)
+ if r == o.Value {
+ return o, nil
+ }
+ return &Float{Value: r}, nil
+ case token.Less:
+ if o.Value < float64(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.Greater:
+ if o.Value > float64(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.LessEq:
+ if o.Value <= float64(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.GreaterEq:
+ if o.Value >= float64(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ }
+ }
+
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Float) Copy() Object {
+ return &Float{Value: o.Value}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Float) IsFalsy() bool {
+ return math.IsNaN(o.Value)
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Float) Equals(x Object) bool {
+ t, ok := x.(*Float)
+ if !ok {
+ return false
+ }
+
+ return o.Value == t.Value
+}
diff --git a/vendor/github.com/d5/tengo/objects/immautable_array.go b/vendor/github.com/d5/tengo/objects/immautable_array.go
new file mode 100644
index 00000000..f3621e29
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/immautable_array.go
@@ -0,0 +1,109 @@
+package objects
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// ImmutableArray represents an immutable array of objects.
+type ImmutableArray struct {
+ Value []Object
+}
+
+// TypeName returns the name of the type.
+func (o *ImmutableArray) TypeName() string {
+ return "immutable-array"
+}
+
+func (o *ImmutableArray) String() string {
+ var elements []string
+ for _, e := range o.Value {
+ elements = append(elements, e.String())
+ }
+
+ return fmt.Sprintf("[%s]", strings.Join(elements, ", "))
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *ImmutableArray) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ if rhs, ok := rhs.(*ImmutableArray); ok {
+ switch op {
+ case token.Add:
+ return &Array{Value: append(o.Value, rhs.Value...)}, nil
+ }
+ }
+
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *ImmutableArray) Copy() Object {
+ var c []Object
+ for _, elem := range o.Value {
+ c = append(c, elem.Copy())
+ }
+
+ return &Array{Value: c}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *ImmutableArray) IsFalsy() bool {
+ return len(o.Value) == 0
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *ImmutableArray) Equals(x Object) bool {
+ var xVal []Object
+ switch x := x.(type) {
+ case *Array:
+ xVal = x.Value
+ case *ImmutableArray:
+ xVal = x.Value
+ default:
+ return false
+ }
+
+ if len(o.Value) != len(xVal) {
+ return false
+ }
+
+ for i, e := range o.Value {
+ if !e.Equals(xVal[i]) {
+ return false
+ }
+ }
+
+ return true
+}
+
+// IndexGet returns an element at a given index.
+func (o *ImmutableArray) IndexGet(index Object) (res Object, err error) {
+ intIdx, ok := index.(*Int)
+ if !ok {
+ err = ErrInvalidIndexType
+ return
+ }
+
+ idxVal := int(intIdx.Value)
+
+ if idxVal < 0 || idxVal >= len(o.Value) {
+ res = UndefinedValue
+ return
+ }
+
+ res = o.Value[idxVal]
+
+ return
+}
+
+// Iterate creates an array iterator.
+func (o *ImmutableArray) Iterate() Iterator {
+ return &ArrayIterator{
+ v: o.Value,
+ l: len(o.Value),
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/immutable_map.go b/vendor/github.com/d5/tengo/objects/immutable_map.go
new file mode 100644
index 00000000..8f58701b
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/immutable_map.go
@@ -0,0 +1,105 @@
+package objects
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// ImmutableMap represents an immutable map object.
+type ImmutableMap struct {
+ Value map[string]Object
+}
+
+// TypeName returns the name of the type.
+func (o *ImmutableMap) TypeName() string {
+ return "immutable-map"
+}
+
+func (o *ImmutableMap) String() string {
+ var pairs []string
+ for k, v := range o.Value {
+ pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String()))
+ }
+
+ return fmt.Sprintf("{%s}", strings.Join(pairs, ", "))
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *ImmutableMap) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *ImmutableMap) Copy() Object {
+ c := make(map[string]Object)
+ for k, v := range o.Value {
+ c[k] = v.Copy()
+ }
+
+ return &Map{Value: c}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *ImmutableMap) IsFalsy() bool {
+ return len(o.Value) == 0
+}
+
+// IndexGet returns the value for the given key.
+func (o *ImmutableMap) IndexGet(index Object) (res Object, err error) {
+ strIdx, ok := ToString(index)
+ if !ok {
+ err = ErrInvalidIndexType
+ return
+ }
+
+ val, ok := o.Value[strIdx]
+ if !ok {
+ val = UndefinedValue
+ }
+
+ return val, nil
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *ImmutableMap) Equals(x Object) bool {
+ var xVal map[string]Object
+ switch x := x.(type) {
+ case *Map:
+ xVal = x.Value
+ case *ImmutableMap:
+ xVal = x.Value
+ default:
+ return false
+ }
+
+ if len(o.Value) != len(xVal) {
+ return false
+ }
+
+ for k, v := range o.Value {
+ tv := xVal[k]
+ if !v.Equals(tv) {
+ return false
+ }
+ }
+
+ return true
+}
+
+// Iterate creates an immutable map iterator.
+func (o *ImmutableMap) Iterate() Iterator {
+ var keys []string
+ for k := range o.Value {
+ keys = append(keys, k)
+ }
+
+ return &MapIterator{
+ v: o.Value,
+ k: keys,
+ l: len(keys),
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/index_assignable.go b/vendor/github.com/d5/tengo/objects/index_assignable.go
new file mode 100644
index 00000000..a1c6cbff
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/index_assignable.go
@@ -0,0 +1,9 @@
+package objects
+
+// IndexAssignable is an object that can take an index and a value
+// on the left-hand side of the assignment statement.
+type IndexAssignable interface {
+ // IndexSet should take an index Object and a value Object.
+ // If an error is returned, it will be treated as a run-time error.
+ IndexSet(index, value Object) error
+}
diff --git a/vendor/github.com/d5/tengo/objects/indexable.go b/vendor/github.com/d5/tengo/objects/indexable.go
new file mode 100644
index 00000000..bbc81633
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/indexable.go
@@ -0,0 +1,9 @@
+package objects
+
+// Indexable is an object that can take an index and return an object.
+type Indexable interface {
+ // IndexGet should take an index Object and return a result Object or an error.
+ // If error is returned, the runtime will treat it as a run-time error and ignore returned value.
+ // If nil is returned as value, it will be converted to Undefined value by the runtime.
+ IndexGet(index Object) (value Object, err error)
+}
diff --git a/vendor/github.com/d5/tengo/objects/int.go b/vendor/github.com/d5/tengo/objects/int.go
new file mode 100644
index 00000000..e902c93a
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/int.go
@@ -0,0 +1,198 @@
+package objects
+
+import (
+ "strconv"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Int represents an integer value.
+type Int struct {
+ Value int64
+}
+
+func (o *Int) String() string {
+ return strconv.FormatInt(o.Value, 10)
+}
+
+// TypeName returns the name of the type.
+func (o *Int) TypeName() string {
+ return "int"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ switch rhs := rhs.(type) {
+ case *Int:
+ switch op {
+ case token.Add:
+ r := o.Value + rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Sub:
+ r := o.Value - rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Mul:
+ r := o.Value * rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Quo:
+ r := o.Value / rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Rem:
+ r := o.Value % rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.And:
+ r := o.Value & rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Or:
+ r := o.Value | rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Xor:
+ r := o.Value ^ rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.AndNot:
+ r := o.Value &^ rhs.Value
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Shl:
+ r := o.Value << uint64(rhs.Value)
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Shr:
+ r := o.Value >> uint64(rhs.Value)
+ if r == o.Value {
+ return o, nil
+ }
+ return &Int{Value: r}, nil
+ case token.Less:
+ if o.Value < rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.Greater:
+ if o.Value > rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.LessEq:
+ if o.Value <= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.GreaterEq:
+ if o.Value >= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ }
+ case *Float:
+ switch op {
+ case token.Add:
+ return &Float{float64(o.Value) + rhs.Value}, nil
+ case token.Sub:
+ return &Float{float64(o.Value) - rhs.Value}, nil
+ case token.Mul:
+ return &Float{float64(o.Value) * rhs.Value}, nil
+ case token.Quo:
+ return &Float{float64(o.Value) / rhs.Value}, nil
+ case token.Less:
+ if float64(o.Value) < rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.Greater:
+ if float64(o.Value) > rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.LessEq:
+ if float64(o.Value) <= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.GreaterEq:
+ if float64(o.Value) >= rhs.Value {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ }
+ case *Char:
+ switch op {
+ case token.Add:
+ return &Char{rune(o.Value) + rhs.Value}, nil
+ case token.Sub:
+ return &Char{rune(o.Value) - rhs.Value}, nil
+ case token.Less:
+ if o.Value < int64(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.Greater:
+ if o.Value > int64(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.LessEq:
+ if o.Value <= int64(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.GreaterEq:
+ if o.Value >= int64(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ }
+ }
+
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Int) Copy() Object {
+ return &Int{Value: o.Value}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Int) IsFalsy() bool {
+ return o.Value == 0
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Int) Equals(x Object) bool {
+ t, ok := x.(*Int)
+ if !ok {
+ return false
+ }
+
+ return o.Value == t.Value
+}
diff --git a/vendor/github.com/d5/tengo/objects/iterable.go b/vendor/github.com/d5/tengo/objects/iterable.go
new file mode 100644
index 00000000..e431d3d7
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/iterable.go
@@ -0,0 +1,7 @@
+package objects
+
+// Iterable represents an object that has iterator.
+type Iterable interface {
+ // Iterate should return an Iterator for the type.
+ Iterate() Iterator
+}
diff --git a/vendor/github.com/d5/tengo/objects/iterator.go b/vendor/github.com/d5/tengo/objects/iterator.go
new file mode 100644
index 00000000..01522ba5
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/iterator.go
@@ -0,0 +1,15 @@
+package objects
+
+// Iterator represents an iterator for underlying data type.
+type Iterator interface {
+ Object
+
+ // Next returns true if there are more elements to iterate.
+ Next() bool
+
+ // Key returns the key or index value of the current element.
+ Key() Object
+
+ // Value returns the value of the current element.
+ Value() Object
+}
diff --git a/vendor/github.com/d5/tengo/objects/map.go b/vendor/github.com/d5/tengo/objects/map.go
new file mode 100644
index 00000000..c42ffe93
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/map.go
@@ -0,0 +1,118 @@
+package objects
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Map represents a map of objects.
+type Map struct {
+ Value map[string]Object
+}
+
+// TypeName returns the name of the type.
+func (o *Map) TypeName() string {
+ return "map"
+}
+
+func (o *Map) String() string {
+ var pairs []string
+ for k, v := range o.Value {
+ pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String()))
+ }
+
+ return fmt.Sprintf("{%s}", strings.Join(pairs, ", "))
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Map) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Map) Copy() Object {
+ c := make(map[string]Object)
+ for k, v := range o.Value {
+ c[k] = v.Copy()
+ }
+
+ return &Map{Value: c}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Map) IsFalsy() bool {
+ return len(o.Value) == 0
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Map) Equals(x Object) bool {
+ var xVal map[string]Object
+ switch x := x.(type) {
+ case *Map:
+ xVal = x.Value
+ case *ImmutableMap:
+ xVal = x.Value
+ default:
+ return false
+ }
+
+ if len(o.Value) != len(xVal) {
+ return false
+ }
+
+ for k, v := range o.Value {
+ tv := xVal[k]
+ if !v.Equals(tv) {
+ return false
+ }
+ }
+
+ return true
+}
+
+// IndexGet returns the value for the given key.
+func (o *Map) IndexGet(index Object) (res Object, err error) {
+ strIdx, ok := index.(*String)
+ if !ok {
+ err = ErrInvalidIndexType
+ return
+ }
+
+ val, ok := o.Value[strIdx.Value]
+ if !ok {
+ val = UndefinedValue
+ }
+
+ return val, nil
+}
+
+// IndexSet sets the value for the given key.
+func (o *Map) IndexSet(index, value Object) (err error) {
+ strIdx, ok := ToString(index)
+ if !ok {
+ err = ErrInvalidIndexType
+ return
+ }
+
+ o.Value[strIdx] = value
+
+ return nil
+}
+
+// Iterate creates a map iterator.
+func (o *Map) Iterate() Iterator {
+ var keys []string
+ for k := range o.Value {
+ keys = append(keys, k)
+ }
+
+ return &MapIterator{
+ v: o.Value,
+ k: keys,
+ l: len(keys),
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/map_iterator.go b/vendor/github.com/d5/tengo/objects/map_iterator.go
new file mode 100644
index 00000000..d60dd0e1
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/map_iterator.go
@@ -0,0 +1,62 @@
+package objects
+
+import "github.com/d5/tengo/compiler/token"
+
+// MapIterator represents an iterator for the map.
+type MapIterator struct {
+ v map[string]Object
+ k []string
+ i int
+ l int
+}
+
+// TypeName returns the name of the type.
+func (i *MapIterator) TypeName() string {
+ return "map-iterator"
+}
+
+func (i *MapIterator) String() string {
+ return "<map-iterator>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (i *MapIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (i *MapIterator) IsFalsy() bool {
+ return true
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (i *MapIterator) Equals(Object) bool {
+ return false
+}
+
+// Copy returns a copy of the type.
+func (i *MapIterator) Copy() Object {
+ return &MapIterator{v: i.v, k: i.k, i: i.i, l: i.l}
+}
+
+// Next returns true if there are more elements to iterate.
+func (i *MapIterator) Next() bool {
+ i.i++
+ return i.i <= i.l
+}
+
+// Key returns the key or index value of the current element.
+func (i *MapIterator) Key() Object {
+ k := i.k[i.i-1]
+
+ return &String{Value: k}
+}
+
+// Value returns the value of the current element.
+func (i *MapIterator) Value() Object {
+ k := i.k[i.i-1]
+
+ return i.v[k]
+}
diff --git a/vendor/github.com/d5/tengo/objects/object.go b/vendor/github.com/d5/tengo/objects/object.go
new file mode 100644
index 00000000..4c5aa7ae
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/object.go
@@ -0,0 +1,30 @@
+package objects
+
+import "github.com/d5/tengo/compiler/token"
+
+// Object represents an object in the VM.
+type Object interface {
+ // TypeName should return the name of the type.
+ TypeName() string
+
+ // String should return a string representation of the type's value.
+ String() string
+
+ // BinaryOp should return another object that is the result of
+ // a given binary operator and a right-hand side object.
+ // If BinaryOp returns an error, the VM will treat it as a run-time error.
+ BinaryOp(op token.Token, rhs Object) (Object, error)
+
+ // IsFalsy should return true if the value of the type
+ // should be considered as falsy.
+ IsFalsy() bool
+
+ // Equals should return true if the value of the type
+ // should be considered as equal to the value of another object.
+ Equals(another Object) bool
+
+ // Copy should return a copy of the type (and its value).
+ // Copy function will be used for copy() builtin function
+ // which is expected to deep-copy the values generally.
+ Copy() Object
+}
diff --git a/vendor/github.com/d5/tengo/objects/objects.go b/vendor/github.com/d5/tengo/objects/objects.go
new file mode 100644
index 00000000..f3878b11
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/objects.go
@@ -0,0 +1,12 @@
+package objects
+
+var (
+ // TrueValue represents a true value.
+ TrueValue Object = &Bool{value: true}
+
+ // FalseValue represents a false value.
+ FalseValue Object = &Bool{value: false}
+
+ // UndefinedValue represents an undefined value.
+ UndefinedValue Object = &Undefined{}
+)
diff --git a/vendor/github.com/d5/tengo/objects/return_value.go b/vendor/github.com/d5/tengo/objects/return_value.go
new file mode 100644
index 00000000..f7ef1dc4
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/return_value.go
@@ -0,0 +1,39 @@
+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/string.go b/vendor/github.com/d5/tengo/objects/string.go
new file mode 100644
index 00000000..6a53b44d
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/string.go
@@ -0,0 +1,95 @@
+package objects
+
+import (
+ "strconv"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// String represents a string value.
+type String struct {
+ Value string
+ runeStr []rune
+}
+
+// TypeName returns the name of the type.
+func (o *String) TypeName() string {
+ return "string"
+}
+
+func (o *String) String() string {
+ return strconv.Quote(o.Value)
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ switch op {
+ case token.Add:
+ switch rhs := rhs.(type) {
+ case *String:
+ return &String{Value: o.Value + rhs.Value}, nil
+ default:
+ return &String{Value: o.Value + rhs.String()}, nil
+ }
+ }
+
+ return nil, ErrInvalidOperator
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *String) IsFalsy() bool {
+ return len(o.Value) == 0
+}
+
+// Copy returns a copy of the type.
+func (o *String) Copy() Object {
+ return &String{Value: o.Value}
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *String) Equals(x Object) bool {
+ t, ok := x.(*String)
+ if !ok {
+ return false
+ }
+
+ return o.Value == t.Value
+}
+
+// IndexGet returns a character at a given index.
+func (o *String) IndexGet(index Object) (res Object, err error) {
+ intIdx, ok := index.(*Int)
+ if !ok {
+ err = ErrInvalidIndexType
+ return
+ }
+
+ idxVal := int(intIdx.Value)
+
+ if o.runeStr == nil {
+ o.runeStr = []rune(o.Value)
+ }
+
+ if idxVal < 0 || idxVal >= len(o.runeStr) {
+ res = UndefinedValue
+ return
+ }
+
+ res = &Char{Value: o.runeStr[idxVal]}
+
+ return
+}
+
+// Iterate creates a string iterator.
+func (o *String) Iterate() Iterator {
+ if o.runeStr == nil {
+ o.runeStr = []rune(o.Value)
+ }
+
+ return &StringIterator{
+ v: o.runeStr,
+ l: len(o.runeStr),
+ }
+}
diff --git a/vendor/github.com/d5/tengo/objects/string_iterator.go b/vendor/github.com/d5/tengo/objects/string_iterator.go
new file mode 100644
index 00000000..8bc95eb5
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/string_iterator.go
@@ -0,0 +1,57 @@
+package objects
+
+import "github.com/d5/tengo/compiler/token"
+
+// StringIterator represents an iterator for a string.
+type StringIterator struct {
+ v []rune
+ i int
+ l int
+}
+
+// TypeName returns the name of the type.
+func (i *StringIterator) TypeName() string {
+ return "string-iterator"
+}
+
+func (i *StringIterator) String() string {
+ return "<string-iterator>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (i *StringIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (i *StringIterator) IsFalsy() bool {
+ return true
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (i *StringIterator) Equals(Object) bool {
+ return false
+}
+
+// Copy returns a copy of the type.
+func (i *StringIterator) Copy() Object {
+ return &StringIterator{v: i.v, i: i.i, l: i.l}
+}
+
+// Next returns true if there are more elements to iterate.
+func (i *StringIterator) Next() bool {
+ i.i++
+ return i.i <= i.l
+}
+
+// Key returns the key or index value of the current element.
+func (i *StringIterator) Key() Object {
+ return &Int{Value: int64(i.i - 1)}
+}
+
+// Value returns the value of the current element.
+func (i *StringIterator) Value() Object {
+ return &Char{Value: i.v[i.i-1]}
+}
diff --git a/vendor/github.com/d5/tengo/objects/time.go b/vendor/github.com/d5/tengo/objects/time.go
new file mode 100644
index 00000000..4e783cc8
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/time.go
@@ -0,0 +1,89 @@
+package objects
+
+import (
+ "time"
+
+ "github.com/d5/tengo/compiler/token"
+)
+
+// Time represents a time value.
+type Time struct {
+ Value time.Time
+}
+
+func (o *Time) String() string {
+ return o.Value.String()
+}
+
+// TypeName returns the name of the type.
+func (o *Time) TypeName() string {
+ return "time"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Time) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ switch rhs := rhs.(type) {
+ case *Int:
+ switch op {
+ case token.Add: // time + int => time
+ if rhs.Value == 0 {
+ return o, nil
+ }
+ return &Time{Value: o.Value.Add(time.Duration(rhs.Value))}, nil
+ case token.Sub: // time - int => time
+ if rhs.Value == 0 {
+ return o, nil
+ }
+ return &Time{Value: o.Value.Add(time.Duration(-rhs.Value))}, nil
+ }
+ case *Time:
+ switch op {
+ case token.Sub: // time - time => int (duration)
+ return &Int{Value: int64(o.Value.Sub(rhs.Value))}, nil
+ case token.Less: // time < time => bool
+ if o.Value.Before(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.Greater:
+ if o.Value.After(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.LessEq:
+ if o.Value.Equal(rhs.Value) || o.Value.Before(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ case token.GreaterEq:
+ if o.Value.Equal(rhs.Value) || o.Value.After(rhs.Value) {
+ return TrueValue, nil
+ }
+ return FalseValue, nil
+ }
+ }
+
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Time) Copy() Object {
+ return &Time{Value: o.Value}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Time) IsFalsy() bool {
+ return o.Value.IsZero()
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Time) Equals(x Object) bool {
+ t, ok := x.(*Time)
+ if !ok {
+ return false
+ }
+
+ return o.Value.Equal(t.Value)
+}
diff --git a/vendor/github.com/d5/tengo/objects/undefined.go b/vendor/github.com/d5/tengo/objects/undefined.go
new file mode 100644
index 00000000..79a380f5
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/undefined.go
@@ -0,0 +1,42 @@
+package objects
+
+import "github.com/d5/tengo/compiler/token"
+
+// Undefined represents an undefined value.
+type Undefined struct{}
+
+// TypeName returns the name of the type.
+func (o *Undefined) TypeName() string {
+ return "undefined"
+}
+
+func (o *Undefined) String() string {
+ return "<undefined>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *Undefined) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *Undefined) Copy() Object {
+ return o
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *Undefined) IsFalsy() bool {
+ return true
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *Undefined) Equals(x Object) bool {
+ return o == x
+}
+
+// IndexGet returns an element at a given index.
+func (o *Undefined) IndexGet(index Object) (Object, error) {
+ return UndefinedValue, nil
+}
diff --git a/vendor/github.com/d5/tengo/objects/user_function.go b/vendor/github.com/d5/tengo/objects/user_function.go
new file mode 100644
index 00000000..1d9bb4f7
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/user_function.go
@@ -0,0 +1,47 @@
+package objects
+
+import (
+ "github.com/d5/tengo/compiler/token"
+)
+
+// UserFunction represents a user function.
+type UserFunction struct {
+ Name string
+ Value CallableFunc
+}
+
+// TypeName returns the name of the type.
+func (o *UserFunction) TypeName() string {
+ return "user-function:" + o.Name
+}
+
+func (o *UserFunction) String() string {
+ return "<user-function>"
+}
+
+// BinaryOp returns another object that is the result of
+// a given binary operator and a right-hand side object.
+func (o *UserFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
+ return nil, ErrInvalidOperator
+}
+
+// Copy returns a copy of the type.
+func (o *UserFunction) Copy() Object {
+ return &UserFunction{Value: o.Value}
+}
+
+// IsFalsy returns true if the value of the type is falsy.
+func (o *UserFunction) IsFalsy() bool {
+ return false
+}
+
+// Equals returns true if the value of the type
+// is equal to the value of another object.
+func (o *UserFunction) Equals(x Object) bool {
+ return false
+}
+
+// Call invokes a user function.
+func (o *UserFunction) Call(args ...Object) (Object, error) {
+ return o.Value(args...)
+}