mirror of
https://github.com/42wim/matterbridge.git
synced 2024-12-22 11:12:44 +01:00
307 lines
6.1 KiB
Go
307 lines
6.1 KiB
Go
package tengo
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
// MaxStringLen is the maximum byte-length for string value. Note this
|
|
// limit applies to all compiler/VM instances in the process.
|
|
MaxStringLen = 2147483647
|
|
|
|
// MaxBytesLen is the maximum length for bytes value. Note this limit
|
|
// applies to all compiler/VM instances in the process.
|
|
MaxBytesLen = 2147483647
|
|
)
|
|
|
|
const (
|
|
// GlobalsSize is the maximum number of global variables for a VM.
|
|
GlobalsSize = 1024
|
|
|
|
// StackSize is the maximum stack size for a VM.
|
|
StackSize = 2048
|
|
|
|
// MaxFrames is the maximum number of function frames for a VM.
|
|
MaxFrames = 1024
|
|
)
|
|
|
|
// CallableFunc is a function signature for the callable functions.
|
|
type CallableFunc = func(args ...Object) (ret Object, err error)
|
|
|
|
// 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
|
|
}
|
|
|
|
// ToString will try to convert object o to string value.
|
|
func ToString(o Object) (v string, ok bool) {
|
|
if o == UndefinedValue {
|
|
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
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
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 = o.Value
|
|
ok = true
|
|
}
|
|
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
|
|
}
|
|
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
|
|
}
|
|
return
|
|
}
|
|
|
|
// 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
|
|
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] = 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] = 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
|
|
}
|
|
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:
|
|
if len(v) > MaxStringLen {
|
|
return nil, ErrStringLimit
|
|
}
|
|
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:
|
|
if len(v) > MaxBytesLen {
|
|
return nil, ErrBytesLimit
|
|
}
|
|
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))
|
|
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
|
|
case CallableFunc:
|
|
return &UserFunction{Value: v}, nil
|
|
}
|
|
return nil, fmt.Errorf("cannot convert to object: %T", v)
|
|
}
|