summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/v2/objects.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/d5/tengo/v2/objects.go')
-rw-r--r--vendor/github.com/d5/tengo/v2/objects.go1581
1 files changed, 1581 insertions, 0 deletions
diff --git a/vendor/github.com/d5/tengo/v2/objects.go b/vendor/github.com/d5/tengo/v2/objects.go
new file mode 100644
index 00000000..27c1d493
--- /dev/null
+++ b/vendor/github.com/d5/tengo/v2/objects.go
@@ -0,0 +1,1581 @@
+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
+ }
+ }
+ 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
+ EncodingID string
+}
+
+// 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}
+}
+
+// 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
+}