add Iterable interface
This commit is contained in:
parent
c7ae181623
commit
2486457fdf
11 changed files with 107 additions and 59 deletions
|
@ -115,3 +115,11 @@ func (o *Array) IndexSet(index, value Object) (err error) {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Iterate creates an array iterator.
|
||||
func (o *Array) Iterate() Iterator {
|
||||
return &ArrayIterator{
|
||||
v: o.Value,
|
||||
l: len(o.Value),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,6 @@ type ArrayIterator struct {
|
|||
l int
|
||||
}
|
||||
|
||||
// NewArrayIterator creates an ArrayIterator.
|
||||
func NewArrayIterator(v *Array) Iterator {
|
||||
return &ArrayIterator{
|
||||
v: v.Value,
|
||||
l: len(v.Value),
|
||||
}
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (i *ArrayIterator) TypeName() string {
|
||||
return "array-iterator"
|
||||
|
|
|
@ -84,3 +84,17 @@ func (o *ImmutableMap) Equals(x Object) bool {
|
|||
|
||||
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 &ImmutableMapIterator{
|
||||
v: o.Value,
|
||||
k: keys,
|
||||
l: len(keys),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,20 +10,6 @@ type ImmutableMapIterator struct {
|
|||
l int
|
||||
}
|
||||
|
||||
// NewModuleMapIterator creates a module iterator.
|
||||
func NewModuleMapIterator(v *ImmutableMap) Iterator {
|
||||
var keys []string
|
||||
for k := range v.Value {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
return &ImmutableMapIterator{
|
||||
v: v.Value,
|
||||
k: keys,
|
||||
l: len(keys),
|
||||
}
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (i *ImmutableMapIterator) TypeName() string {
|
||||
return "module-iterator"
|
||||
|
|
7
objects/iterable.go
Normal file
7
objects/iterable.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package objects
|
||||
|
||||
// Iterable represents an object that has iterator.
|
||||
type Iterable interface {
|
||||
// Iterate should return an Iterator for the type.
|
||||
Iterate() Iterator
|
||||
}
|
|
@ -97,3 +97,17 @@ func (o *Map) IndexSet(index, value Object) (err error) {
|
|||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,20 +10,6 @@ type MapIterator struct {
|
|||
l int
|
||||
}
|
||||
|
||||
// NewMapIterator creates a map iterator.
|
||||
func NewMapIterator(v *Map) Iterator {
|
||||
var keys []string
|
||||
for k := range v.Value {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
return &MapIterator{
|
||||
v: v.Value,
|
||||
k: keys,
|
||||
l: len(keys),
|
||||
}
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (i *MapIterator) TypeName() string {
|
||||
return "map-iterator"
|
||||
|
|
|
@ -81,3 +81,15 @@ func (o *String) IndexGet(index Object) (res Object, err error) {
|
|||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,16 +9,6 @@ type StringIterator struct {
|
|||
l int
|
||||
}
|
||||
|
||||
// NewStringIterator creates a string iterator.
|
||||
func NewStringIterator(v *String) Iterator {
|
||||
r := []rune(v.Value)
|
||||
|
||||
return &StringIterator{
|
||||
v: r,
|
||||
l: len(r),
|
||||
}
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (i *StringIterator) TypeName() string {
|
||||
return "string-iterator"
|
||||
|
|
|
@ -921,19 +921,13 @@ func (v *VM) Run() error {
|
|||
dst := v.stack[v.sp-1]
|
||||
v.sp--
|
||||
|
||||
switch dst := (*dst).(type) {
|
||||
case *objects.Array:
|
||||
iterator = objects.NewArrayIterator(dst)
|
||||
case *objects.Map:
|
||||
iterator = objects.NewMapIterator(dst)
|
||||
case *objects.ImmutableMap:
|
||||
iterator = objects.NewModuleMapIterator(dst)
|
||||
case *objects.String:
|
||||
iterator = objects.NewStringIterator(dst)
|
||||
default:
|
||||
return fmt.Errorf("non-iterable type: %s", dst.TypeName())
|
||||
iterable, ok := (*dst).(objects.Iterable)
|
||||
if !ok {
|
||||
return fmt.Errorf("non-iterable type: %s", (*dst).TypeName())
|
||||
}
|
||||
|
||||
iterator = iterable.Iterate()
|
||||
|
||||
if v.sp >= StackSize {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
|
@ -945,12 +939,13 @@ func (v *VM) Run() error {
|
|||
iterator := v.stack[v.sp-1]
|
||||
v.sp--
|
||||
|
||||
b := (*iterator).(objects.Iterator).Next()
|
||||
hasMore := (*iterator).(objects.Iterator).Next()
|
||||
|
||||
if v.sp >= StackSize {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
|
||||
if b {
|
||||
if hasMore {
|
||||
v.stack[v.sp] = truePtr
|
||||
} else {
|
||||
v.stack[v.sp] = falsePtr
|
||||
|
|
44
runtime/vm_iterable_test.go
Normal file
44
runtime/vm_iterable_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package runtime_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
type StringArrayIterator struct {
|
||||
objectImpl
|
||||
strArr *StringArray
|
||||
idx int
|
||||
}
|
||||
|
||||
func (i *StringArrayIterator) TypeName() string {
|
||||
return "string-array-iterator"
|
||||
}
|
||||
|
||||
func (i *StringArrayIterator) Next() bool {
|
||||
i.idx++
|
||||
return i.idx <= len(i.strArr.Value)
|
||||
}
|
||||
|
||||
func (i *StringArrayIterator) Key() objects.Object {
|
||||
return &objects.Int{Value: int64(i.idx - 1)}
|
||||
}
|
||||
|
||||
func (i *StringArrayIterator) Value() objects.Object {
|
||||
return &objects.String{Value: i.strArr.Value[i.idx-1]}
|
||||
}
|
||||
|
||||
func (o *StringArray) Iterate() objects.Iterator {
|
||||
return &StringArrayIterator{
|
||||
strArr: o,
|
||||
}
|
||||
}
|
||||
|
||||
func TestIterable(t *testing.T) {
|
||||
strArr := func() *StringArray { return &StringArray{Value: []string{"one", "two", "three"}} }
|
||||
|
||||
expectWithSymbols(t, `for i, s in arr { out += i }`, 3, SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `for i, s in arr { out += s }`, "onetwothree", SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `for i, s in arr { out += s + i }`, "one0two1three2", SYM{"arr": strArr()})
|
||||
}
|
Loading…
Reference in a new issue