2019-01-31 11:39:45 +03:00
|
|
|
# Object Types
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
## Table of Contents
|
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
- [Tengo Objects](#tengo-objects)
|
|
|
|
- [Runtime Object Types](#runtime-object-types)
|
|
|
|
- [User Object Types](#user-object-types)
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
## Tengo Objects
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
In Tengo, all object types _(both [runtime types](#runtime-object-types) and
|
|
|
|
[user types](#user-object-types))_ must implement
|
|
|
|
[Object](https://godoc.org/github.com/d5/tengo#Object) interface.
|
2019-01-31 11:39:45 +03:00
|
|
|
|
|
|
|
### Object Interface
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
|
|
|
TypeName() string
|
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
TypeName method should return the name of the type. Type names are not directly
|
|
|
|
used by the runtime _(except when it reports a run-time error)_, but, it is
|
|
|
|
generally a good idea to keep it short but distinguishable from other types.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
|
|
|
String() string
|
|
|
|
```
|
2020-03-04 17:53:38 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
String method should return a string representation of the underlying value.
|
|
|
|
The value returned by String method will be used whenever string formatting for
|
|
|
|
the value is required, most commonly when being converted into String value.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
|
|
|
BinaryOp(op token.Token, rhs Object) (res Object, err error)
|
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
In Tengo, a type can overload binary operators
|
|
|
|
(`+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `&^`, `>>`, `<<`, `>`, `>=`; _note
|
|
|
|
that `<` and `<=` operators are not overloadable as they're simply implemented
|
|
|
|
by switching left-hand side and right-hand side of `>`/`>=` operator_) by
|
|
|
|
implementing BinaryOp method. BinaryOp method takes the operator `op` and the
|
|
|
|
right-hand side object `rhs`, and, should return a resulting value `res`.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
**Error value vs runtime error**
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
If BinaryOp method returns an error `err` (the second return value), it will be
|
|
|
|
treated as a run-time error, which will halt the execution (`VM.Run() error`)
|
|
|
|
and will return the error to the user. All runtime type implementations, for
|
|
|
|
example, will return an `ErrInvalidOperator` error when the given operator is
|
2020-03-04 17:53:38 +03:00
|
|
|
not supported by the type.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Alternatively the method can return an `Error` value as its result `res`
|
|
|
|
(the first return value), which will not halt the runtime and will be treated
|
|
|
|
like any other values. As a dynamically typed language, the receiver (another
|
|
|
|
expression or statement) can determine how to translate `Error` value returned
|
2020-03-04 17:53:38 +03:00
|
|
|
from binary operator expression.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
|
|
|
IsFalsy() bool
|
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
IsFalsy method should return true if the underlying value is considered to be
|
|
|
|
[falsy](https://github.com/d5/tengo/blob/master/docs/runtime-types.md#objectisfalsy).
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
|
|
|
Equals(o Object) bool
|
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Equals method should return true if the underlying value is considered to be
|
|
|
|
equal to the underlying value of another object `o`. When comparing values of
|
|
|
|
different types, the runtime does not guarantee or force anything, but, it's
|
|
|
|
generally a good idea to make the result consistent. For example, a custom
|
|
|
|
integer type may return true when comparing against String value, but, it
|
|
|
|
should return the same result for the same inputs.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
|
|
|
Copy() Object
|
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Copy method should return a _new_ copy of the object. Builtin function `copy`
|
|
|
|
uses this method to copy values. Default implementation of all runtime types
|
|
|
|
return a deep-copy values, but, it's not a requirement by the runtime.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
```golang
|
|
|
|
IndexGet(index Object) (value Object, err error)
|
|
|
|
```
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
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 a type is indexable, its values support dot selector
|
|
|
|
(value = object.index) and indexer (value = object[index]) syntax.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
If Object is not indexable, ErrNotIndexable should be returned as error. If nil
|
|
|
|
is returned as value, it will be converted to Undefined value by the runtime.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
If `IndexGet` returns an error (`err`), the VM will treat it as a run-time
|
|
|
|
error and ignore the returned value.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Array and Map implementation forces the type of index Object to be Int and
|
|
|
|
String respectively, but, it's not a required behavior of the VM. It is
|
|
|
|
completely okay to take various index types as long as it is consistent.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
By convention, Array or Array-like types and Map or Map-like types return
|
|
|
|
`Undefined` value when the key does not exist. But, again, this is not a
|
|
|
|
required behavior.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
2019-12-20 22:40:38 +03:00
|
|
|
IndexSet(index, value Object) error
|
2019-01-31 11:39:45 +03:00
|
|
|
```
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
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 a type is index assignable,
|
|
|
|
its values support assignment using dot selector (`object.index = value`) and
|
|
|
|
indexer (`object[index] = value`) in the assignment statements.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
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.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Array and Map implementation forces the type of index Object to be Int and
|
|
|
|
String respectively, but, it's not a required behavior of the VM. It is
|
|
|
|
completely okay to take various index types as long as it is consistent.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
#### Callable Objects
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
If the type is Callable, its values can be invoked as if they were functions.
|
|
|
|
Two functions need to be implemented for Callable objects.
|
2019-01-31 11:39:45 +03:00
|
|
|
|
|
|
|
```golang
|
2019-12-20 22:40:38 +03:00
|
|
|
CanCall() bool
|
|
|
|
```
|
|
|
|
|
|
|
|
CanCall should return whether the Object can be called. When this function
|
|
|
|
returns true, the Object is considered Callable.
|
|
|
|
|
|
|
|
```golang
|
|
|
|
Call(args ...Object) (ret Object, err error)
|
2019-01-31 11:39:45 +03:00
|
|
|
```
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Call should take an arbitrary number of arguments and return a return value
|
|
|
|
and/or an error, which the VM will consider as a run-time error.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
#### Iterable Objects
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
If a type is iterable, its values can be used in `for-in` statements
|
|
|
|
(`for key, value in object { ... }`). Two functions need to be implemented
|
|
|
|
for Iterable Objects
|
2019-01-31 11:39:45 +03:00
|
|
|
|
|
|
|
```golang
|
2019-12-20 22:40:38 +03:00
|
|
|
CanIterate() bool
|
2019-01-24 02:39:05 +03:00
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
CanIterate should return whether the Object can be Iterated.
|
2019-01-31 11:39:45 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
```golang
|
|
|
|
Iterate() Iterator
|
|
|
|
```
|
|
|
|
|
|
|
|
The Iterate method should return another object that implements
|
|
|
|
[Iterator](https://godoc.org/github.com/d5/tengo#Iterator) interface.
|
|
|
|
|
|
|
|
### Iterator Interface
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
2019-01-31 11:39:45 +03:00
|
|
|
Next() bool
|
|
|
|
```
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Next method should return true if there are more elements to iterate. When used
|
|
|
|
with `for-in` statements, the compiler uses Key and Value methods to populate
|
|
|
|
the current element's key (or index) and value from the object that this
|
|
|
|
iterator represents. The runtime will stop iterating in `for-in` statement
|
|
|
|
when this method returns false.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
```golang
|
|
|
|
Key() Object
|
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Key method should return a key (or an index) Object for the current element of
|
|
|
|
the underlying object. It should return the same value until Next method is
|
|
|
|
called again. By convention, iterators for the map or map-like objects returns
|
|
|
|
the String key, and, iterators for array or array-like objects returns the Int
|
|
|
|
ndex. But, it's not a requirement by the VM.
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
```golang
|
|
|
|
Value() Object
|
2019-01-24 02:39:05 +03:00
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Value method should return a value Object for the current element of the
|
|
|
|
underlying object. It should return the same value until Next method is called
|
|
|
|
again.
|
2019-01-31 11:39:45 +03:00
|
|
|
|
|
|
|
## Runtime Object Types
|
|
|
|
|
|
|
|
These are the basic types Tengo runtime supports out of the box:
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
- Primitive value types: [Int](https://godoc.org/github.com/d5/tengo#Int),
|
|
|
|
[String](https://godoc.org/github.com/d5/tengo#String),
|
|
|
|
[Float](https://godoc.org/github.com/d5/tengo#Float),
|
|
|
|
[Bool](https://godoc.org/github.com/d5/tengo#ArrayIterator),
|
|
|
|
[Char](https://godoc.org/github.com/d5/tengo#Char),
|
|
|
|
[Bytes](https://godoc.org/github.com/d5/tengo#Bytes),
|
|
|
|
[Time](https://godoc.org/github.com/d5/tengo#Time)
|
|
|
|
- Composite value types: [Array](https://godoc.org/github.com/d5/tengo#Array),
|
|
|
|
[ImmutableArray](https://godoc.org/github.com/d5/tengo#ImmutableArray),
|
|
|
|
[Map](https://godoc.org/github.com/d5/tengo#Map),
|
|
|
|
[ImmutableMap](https://godoc.org/github.com/d5/tengo#ImmutableMap)
|
|
|
|
- Functions:
|
|
|
|
[CompiledFunction](https://godoc.org/github.com/d5/tengo#CompiledFunction),
|
|
|
|
[BuiltinFunction](https://godoc.org/github.com/d5/tengo#BuiltinFunction),
|
|
|
|
[UserFunction](https://godoc.org/github.com/d5/tengo#UserFunction)
|
|
|
|
- [Iterators](https://godoc.org/github.com/d5/tengo#Iterator):
|
|
|
|
[StringIterator](https://godoc.org/github.com/d5/tengo#StringIterator),
|
|
|
|
[ArrayIterator](https://godoc.org/github.com/d5/tengo#ArrayIterator),
|
|
|
|
[MapIterator](https://godoc.org/github.com/d5/tengo#MapIterator),
|
|
|
|
[ImmutableMapIterator](https://godoc.org/github.com/d5/tengo#ImmutableMapIterator)
|
|
|
|
- [Error](https://godoc.org/github.com/d5/tengo#Error)
|
|
|
|
- [Undefined](https://godoc.org/github.com/d5/tengo#Undefined)
|
|
|
|
- Other internal objects: [Break](https://godoc.org/github.com/d5/tengo#Break),
|
|
|
|
[Continue](https://godoc.org/github.com/d5/tengo#Continue),
|
|
|
|
[ReturnValue](https://godoc.org/github.com/d5/tengo#ReturnValue)
|
|
|
|
|
|
|
|
See
|
|
|
|
[Runtime Types](https://github.com/d5/tengo/blob/master/docs/runtime-types.md)
|
|
|
|
for more details on these runtime types.
|
2019-01-31 11:39:45 +03:00
|
|
|
|
|
|
|
## User Object Types
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
Users can easily extend and add their own types by implementing the same
|
|
|
|
[Object](https://godoc.org/github.com/d5/tengo#Object) interface and the
|
|
|
|
default `ObjectImpl` implementation. Tengo runtime will treat them in the
|
|
|
|
same way as its runtime types with no performance overhead.
|
2019-01-31 11:39:45 +03:00
|
|
|
|
|
|
|
Here's an example user type implementation, `StringArray`:
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
2019-01-31 11:39:45 +03:00
|
|
|
type StringArray struct {
|
2020-03-04 17:53:38 +03:00
|
|
|
tengo.ObjectImpl
|
|
|
|
Value []string
|
2019-01-24 02:39:05 +03:00
|
|
|
}
|
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
func (o *StringArray) String() string {
|
2020-03-04 17:53:38 +03:00
|
|
|
return strings.Join(o.Value, ", ")
|
2019-01-31 11:39:45 +03:00
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
func (o *StringArray) BinaryOp(op token.Token, rhs tengo.Object) (tengo.Object, error) {
|
2020-03-04 17:53:38 +03:00
|
|
|
if rhs, ok := rhs.(*StringArray); ok {
|
|
|
|
switch op {
|
|
|
|
case token.Add:
|
|
|
|
if len(rhs.Value) == 0 {
|
|
|
|
return o, nil
|
|
|
|
}
|
|
|
|
return &StringArray{Value: append(o.Value, rhs.Value...)}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, tengo.ErrInvalidOperator
|
2019-01-31 11:39:45 +03:00
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
func (o *StringArray) IsFalsy() bool {
|
2020-03-04 17:53:38 +03:00
|
|
|
return len(o.Value) == 0
|
2019-01-24 02:39:05 +03:00
|
|
|
}
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
func (o *StringArray) Equals(x tengo.Object) bool {
|
2020-03-04 17:53:38 +03:00
|
|
|
if x, ok := x.(*StringArray); ok {
|
|
|
|
if len(o.Value) != len(x.Value) {
|
|
|
|
return false
|
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2020-03-04 17:53:38 +03:00
|
|
|
for i, v := range o.Value {
|
|
|
|
if v != x.Value[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2020-03-04 17:53:38 +03:00
|
|
|
return true
|
|
|
|
}
|
2019-01-31 11:39:45 +03:00
|
|
|
|
2020-03-04 17:53:38 +03:00
|
|
|
return false
|
2019-01-24 02:39:05 +03:00
|
|
|
}
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
func (o *StringArray) Copy() tengo.Object {
|
2020-03-04 17:53:38 +03:00
|
|
|
return &StringArray{
|
|
|
|
Value: append([]string{}, o.Value...),
|
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
}
|
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
func (o *StringArray) TypeName() string {
|
2020-03-04 17:53:38 +03:00
|
|
|
return "string-array"
|
2019-01-31 11:39:45 +03:00
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
You can use a user type via either
|
|
|
|
[Script.Add](https://godoc.org/github.com/d5/tengo#Script.Add) or by directly
|
|
|
|
manipulating the symbol table and the global variables. Here's an example code
|
|
|
|
to add `StringArray` to the script:
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
2019-01-31 11:39:45 +03:00
|
|
|
// script that uses 'my_list'
|
2019-12-20 22:40:38 +03:00
|
|
|
s := tengo.NewScript([]byte(`
|
2020-03-04 17:53:38 +03:00
|
|
|
print(my_list + "three")
|
2019-01-31 11:39:45 +03:00
|
|
|
`))
|
|
|
|
|
|
|
|
myList := &StringArray{Value: []string{"one", "two"}}
|
2019-12-20 22:40:38 +03:00
|
|
|
s.Add("my_list", myList) // add StringArray value 'my_list'
|
|
|
|
s.Run() // prints "one, two, three"
|
2019-01-24 02:39:05 +03:00
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
It can also implement `IndexGet` and `IndexSet`:
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
2019-12-20 22:40:38 +03:00
|
|
|
func (o *StringArray) IndexGet(index tengo.Object) (tengo.Object, error) {
|
2020-03-04 17:53:38 +03:00
|
|
|
intIdx, ok := index.(*tengo.Int)
|
|
|
|
if ok {
|
|
|
|
if intIdx.Value >= 0 && intIdx.Value < int64(len(o.Value)) {
|
|
|
|
return &tengo.String{Value: o.Value[intIdx.Value]}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, tengo.ErrIndexOutOfBounds
|
|
|
|
}
|
|
|
|
|
|
|
|
strIdx, ok := index.(*tengo.String)
|
|
|
|
if ok {
|
|
|
|
for vidx, str := range o.Value {
|
|
|
|
if strIdx.Value == str {
|
|
|
|
return &tengo.Int{Value: int64(vidx)}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tengo.UndefinedValue, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, tengo.ErrInvalidIndexType
|
2019-01-24 02:39:05 +03:00
|
|
|
}
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
func (o *StringArray) IndexSet(index, value tengo.Object) error {
|
2020-03-04 17:53:38 +03:00
|
|
|
strVal, ok := tengo.ToString(value)
|
|
|
|
if !ok {
|
|
|
|
return tengo.ErrInvalidIndexValueType
|
|
|
|
}
|
|
|
|
|
|
|
|
intIdx, ok := index.(*tengo.Int)
|
|
|
|
if ok {
|
|
|
|
if intIdx.Value >= 0 && intIdx.Value < int64(len(o.Value)) {
|
|
|
|
o.Value[intIdx.Value] = strVal
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return tengo.ErrIndexOutOfBounds
|
|
|
|
}
|
|
|
|
|
|
|
|
return tengo.ErrInvalidIndexType
|
2019-01-24 02:39:05 +03:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2020-03-09 16:40:39 +03:00
|
|
|
If we implement `CanCall` and `Call`:
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
2019-12-20 22:40:38 +03:00
|
|
|
func (o *StringArray) CanCall() bool {
|
2020-03-04 17:53:38 +03:00
|
|
|
return true
|
2019-12-20 22:40:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (o *StringArray) Call(args ...tengo.Object) (ret tengo.Object, err error) {
|
2020-03-04 17:53:38 +03:00
|
|
|
if len(args) != 1 {
|
|
|
|
return nil, tengo.ErrWrongNumArguments
|
|
|
|
}
|
|
|
|
|
|
|
|
s1, ok := tengo.ToString(args[0])
|
|
|
|
if !ok {
|
|
|
|
return nil, tengo.ErrInvalidArgumentType{
|
|
|
|
Name: "first",
|
|
|
|
Expected: "string",
|
|
|
|
Found: args[0].TypeName(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, v := range o.Value {
|
|
|
|
if v == s1 {
|
|
|
|
return &tengo.Int{Value: int64(i)}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tengo.UndefinedValue, nil
|
2019-01-24 02:39:05 +03:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
Then it can be "invoked":
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
```golang
|
2019-12-20 22:40:38 +03:00
|
|
|
s := tengo.NewScript([]byte(`
|
2020-03-04 17:53:38 +03:00
|
|
|
print(my_list("two"))
|
2019-01-31 11:39:45 +03:00
|
|
|
`))
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
myList := &StringArray{Value: []string{"one", "two", "three"}}
|
2019-12-20 22:40:38 +03:00
|
|
|
s.Add("my_list", myList) // add StringArray value 'my_list'
|
2019-01-31 11:39:45 +03:00
|
|
|
s.Run() // prints "1" (index of "two")
|
|
|
|
```
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
We can also make `StringArray` iterable:
|
2019-01-24 02:39:05 +03:00
|
|
|
|
|
|
|
```golang
|
2019-12-20 22:40:38 +03:00
|
|
|
func (o *StringArray) CanIterate() bool {
|
2020-03-04 17:53:38 +03:00
|
|
|
return true
|
2019-12-20 22:40:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (o *StringArray) Iterate() tengo.Iterator {
|
2020-03-04 17:53:38 +03:00
|
|
|
return &StringArrayIterator{
|
|
|
|
strArr: o,
|
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
}
|
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
type StringArrayIterator struct {
|
2020-03-04 17:53:38 +03:00
|
|
|
tengo.ObjectImpl
|
|
|
|
strArr *StringArray
|
|
|
|
idx int
|
2019-01-31 11:39:45 +03:00
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
func (i *StringArrayIterator) TypeName() string {
|
2020-03-04 17:53:38 +03:00
|
|
|
return "string-array-iterator"
|
2019-01-31 11:39:45 +03:00
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-01-31 11:39:45 +03:00
|
|
|
func (i *StringArrayIterator) Next() bool {
|
2020-03-04 17:53:38 +03:00
|
|
|
i.idx++
|
|
|
|
return i.idx <= len(i.strArr.Value)
|
2019-01-31 11:39:45 +03:00
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
func (i *StringArrayIterator) Key() tengo.Object {
|
2020-03-04 17:53:38 +03:00
|
|
|
return &tengo.Int{Value: int64(i.idx - 1)}
|
2019-01-31 11:39:45 +03:00
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
func (i *StringArrayIterator) Value() tengo.Object {
|
2020-03-04 17:53:38 +03:00
|
|
|
return &tengo.String{Value: i.strArr.Value[i.idx-1]}
|
2019-01-31 11:39:45 +03:00
|
|
|
}
|
2019-01-24 02:39:05 +03:00
|
|
|
```
|
|
|
|
|
2019-12-20 22:40:38 +03:00
|
|
|
### ObjectImpl
|
|
|
|
|
|
|
|
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.
|