add Iterable interface

This commit is contained in:
Daniel Kang 2019-01-23 13:36:03 -08:00
parent c7ae181623
commit 2486457fdf
11 changed files with 107 additions and 59 deletions

View file

@ -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),
}
}

View file

@ -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"

View file

@ -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),
}
}

View file

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

View file

@ -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),
}
}

View file

@ -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"

View file

@ -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),
}
}

View file

@ -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"

View file

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

View 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()})
}