matterbridge/vendor/github.com/d5/tengo/v2/objects.go

1613 lines
35 KiB
Go

package tengo
import (
"bytes"
"fmt"
"math"
"strconv"
"strings"
"time"
"github.com/d5/tengo/v2/parser"
"github.com/d5/tengo/v2/token"
)
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{}
)
// 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
// IndexGet should take an index Object and return a result Object or an
// error for indexable objects. Indexable is an object that can take an
// index and return an object. If error is returned, the runtime will treat
// it as a run-time error and ignore returned value. If Object is not
// indexable, ErrNotIndexable should be returned as error. If nil is
// returned as value, it will be converted to UndefinedToken value by the
// runtime.
IndexGet(index Object) (value Object, err error)
// IndexSet should take an index Object and a value Object for index
// assignable objects. Index assignable is an object that can take an index
// and a value on the left-hand side of the assignment statement. If Object
// is not index assignable, ErrNotIndexAssignable should be returned as
// error. If an error is returned, it will be treated as a run-time error.
IndexSet(index, value Object) error
// Iterate should return an Iterator for the type.
Iterate() Iterator
// CanIterate should return whether the Object can be Iterated.
CanIterate() bool
// 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)
// CanCall should return whether the Object can be Called.
CanCall() bool
}
// ObjectImpl represents a default Object Implementation. To defined a new
// value type, one can embed ObjectImpl in their type declarations to avoid
// implementing all non-significant methods. TypeName() and String() methods
// still need to be implemented.
type ObjectImpl struct {
}
// TypeName returns the name of the type.
func (o *ObjectImpl) TypeName() string {
panic(ErrNotImplemented)
}
func (o *ObjectImpl) String() string {
panic(ErrNotImplemented)
}
// BinaryOp returns another object that is the result of a given binary
// operator and a right-hand side object.
func (o *ObjectImpl) BinaryOp(_ token.Token, _ Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *ObjectImpl) Copy() Object {
return nil
}
// IsFalsy returns true if the value of the type is falsy.
func (o *ObjectImpl) IsFalsy() bool {
return false
}
// Equals returns true if the value of the type is equal to the value of
// another object.
func (o *ObjectImpl) Equals(x Object) bool {
return o == x
}
// IndexGet returns an element at a given index.
func (o *ObjectImpl) IndexGet(_ Object) (res Object, err error) {
return nil, ErrNotIndexable
}
// IndexSet sets an element at a given index.
func (o *ObjectImpl) IndexSet(_, _ Object) (err error) {
return ErrNotIndexAssignable
}
// Iterate returns an iterator.
func (o *ObjectImpl) Iterate() Iterator {
return nil
}
// CanIterate returns whether the Object can be Iterated.
func (o *ObjectImpl) CanIterate() bool {
return false
}
// Call takes an arbitrary number of arguments and returns a return value
// and/or an error.
func (o *ObjectImpl) Call(_ ...Object) (ret Object, err error) {
return nil, nil
}
// CanCall returns whether the Object can be Called.
func (o *ObjectImpl) CanCall() bool {
return false
}
// Array represents an array of objects.
type Array struct {
ObjectImpl
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),
}
}
// CanIterate returns whether the Object can be Iterated.
func (o *Array) CanIterate() bool {
return true
}
// Bool represents a boolean value.
type Bool struct {
ObjectImpl
// 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"
}
// 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
}
// BuiltinFunction represents a builtin function.
type BuiltinFunction struct {
ObjectImpl
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>"
}
// Copy returns a copy of the type.
func (o *BuiltinFunction) Copy() Object {
return &BuiltinFunction{Value: o.Value}
}
// Equals returns true if the value of the type is equal to the value of
// another object.
func (o *BuiltinFunction) Equals(_ Object) bool {
return false
}
// Call executes a builtin function.
func (o *BuiltinFunction) Call(args ...Object) (Object, error) {
return o.Value(args...)
}
// CanCall returns whether the Object can be Called.
func (o *BuiltinFunction) CanCall() bool {
return true
}
// 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}
}
// Bytes represents a byte array.
type Bytes struct {
ObjectImpl
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:
if len(o.Value)+len(rhs.Value) > MaxBytesLen {
return nil, ErrBytesLimit
}
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.Equal(o.Value, t.Value)
}
// 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
}
// Iterate creates a bytes iterator.
func (o *Bytes) Iterate() Iterator {
return &BytesIterator{
v: o.Value,
l: len(o.Value),
}
}
// CanIterate returns whether the Object can be Iterated.
func (o *Bytes) CanIterate() bool {
return true
}
// Char represents a character value.
type Char struct {
ObjectImpl
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
}
// CompiledFunction represents a compiled function.
type CompiledFunction struct {
ObjectImpl
Instructions []byte
NumLocals int // number of local variables (including function parameters)
NumParameters int
VarArgs bool
SourceMap map[int]parser.Pos
Free []*ObjectPtr
}
// TypeName returns the name of the type.
func (o *CompiledFunction) TypeName() string {
return "compiled-function"
}
func (o *CompiledFunction) String() string {
return "<compiled-function>"
}
// 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,
VarArgs: o.VarArgs,
Free: append([]*ObjectPtr{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers
}
}
// Equals returns true if the value of the type is equal to the value of
// another object.
func (o *CompiledFunction) Equals(_ Object) bool {
return false
}
// SourcePos returns the source position of the instruction at ip.
func (o *CompiledFunction) SourcePos(ip int) parser.Pos {
for ip >= 0 {
if p, ok := o.SourceMap[ip]; ok {
return p
}
ip--
}
return parser.NoPos
}
// CanCall returns whether the Object can be Called.
func (o *CompiledFunction) CanCall() bool {
return true
}
// Error represents an error value.
type Error struct {
ObjectImpl
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"
}
// 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
}
// IndexGet returns an element at a given index.
func (o *Error) IndexGet(index Object) (res Object, err error) {
if strIdx, _ := ToString(index); strIdx != "value" {
err = ErrInvalidIndexOnError
return
}
res = o.Value
return
}
// Float represents a floating point number value.
type Float struct {
ObjectImpl
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
}
// ImmutableArray represents an immutable array of objects.
type ImmutableArray struct {
ObjectImpl
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),
}
}
// CanIterate returns whether the Object can be Iterated.
func (o *ImmutableArray) CanIterate() bool {
return true
}
// ImmutableMap represents an immutable map object.
type ImmutableMap struct {
ObjectImpl
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, ", "))
}
// 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
}
res, ok = o.Value[strIdx]
if !ok {
res = UndefinedValue
}
return
}
// 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),
}
}
// CanIterate returns whether the Object can be Iterated.
func (o *ImmutableMap) CanIterate() bool {
return true
}
// Int represents an integer value.
type Int struct {
ObjectImpl
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{Value: float64(o.Value) + rhs.Value}, nil
case token.Sub:
return &Float{Value: float64(o.Value) - rhs.Value}, nil
case token.Mul:
return &Float{Value: float64(o.Value) * rhs.Value}, nil
case token.Quo:
return &Float{Value: 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{Value: rune(o.Value) + rhs.Value}, nil
case token.Sub:
return &Char{Value: 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
}
// Map represents a map of objects.
type Map struct {
ObjectImpl
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, ", "))
}
// 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 := ToString(index)
if !ok {
err = ErrInvalidIndexType
return
}
res, ok = o.Value[strIdx]
if !ok {
res = UndefinedValue
}
return
}
// 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),
}
}
// CanIterate returns whether the Object can be Iterated.
func (o *Map) CanIterate() bool {
return true
}
// ObjectPtr represents a free variable.
type ObjectPtr struct {
ObjectImpl
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>"
}
// 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
}
// String represents a string value.
type String struct {
ObjectImpl
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:
if len(o.Value)+len(rhs.Value) > MaxStringLen {
return nil, ErrStringLimit
}
return &String{Value: o.Value + rhs.Value}, nil
default:
rhsStr := rhs.String()
if len(o.Value)+len(rhsStr) > MaxStringLen {
return nil, ErrStringLimit
}
return &String{Value: o.Value + rhsStr}, nil
}
case token.Less:
switch rhs := rhs.(type) {
case *String:
if o.Value < rhs.Value {
return TrueValue, nil
}
return FalseValue, nil
}
case token.LessEq:
switch rhs := rhs.(type) {
case *String:
if o.Value <= rhs.Value {
return TrueValue, nil
}
return FalseValue, nil
}
case token.Greater:
switch rhs := rhs.(type) {
case *String:
if o.Value > rhs.Value {
return TrueValue, nil
}
return FalseValue, nil
}
case token.GreaterEq:
switch rhs := rhs.(type) {
case *String:
if o.Value >= rhs.Value {
return TrueValue, nil
}
return FalseValue, 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),
}
}
// CanIterate returns whether the Object can be Iterated.
func (o *String) CanIterate() bool {
return true
}
// Time represents a time value.
type Time struct {
ObjectImpl
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)
}
// Undefined represents an undefined value.
type Undefined struct {
ObjectImpl
}
// TypeName returns the name of the type.
func (o *Undefined) TypeName() string {
return "undefined"
}
func (o *Undefined) String() string {
return "<undefined>"
}
// 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(_ Object) (Object, error) {
return UndefinedValue, nil
}
// Iterate creates a map iterator.
func (o *Undefined) Iterate() Iterator {
return o
}
// CanIterate returns whether the Object can be Iterated.
func (o *Undefined) CanIterate() bool {
return true
}
// Next returns true if there are more elements to iterate.
func (o *Undefined) Next() bool {
return false
}
// Key returns the key or index value of the current element.
func (o *Undefined) Key() Object {
return o
}
// Value returns the value of the current element.
func (o *Undefined) Value() Object {
return o
}
// UserFunction represents a user function.
type UserFunction struct {
ObjectImpl
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>"
}
// Copy returns a copy of the type.
func (o *UserFunction) Copy() Object {
return &UserFunction{Value: o.Value, Name: o.Name}
}
// Equals returns true if the value of the type is equal to the value of
// another object.
func (o *UserFunction) Equals(_ Object) bool {
return false
}
// Call invokes a user function.
func (o *UserFunction) Call(args ...Object) (Object, error) {
return o.Value(args...)
}
// CanCall returns whether the Object can be Called.
func (o *UserFunction) CanCall() bool {
return true
}