|
|
|
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
|
|
|
|
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
|
|
|
|
}
|