From 2486457fdf5d60469b67725bcf5e821c68a37754 Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Wed, 23 Jan 2019 13:36:03 -0800 Subject: [PATCH] add Iterable interface --- objects/array.go | 8 ++++++ objects/array_iterator.go | 8 ------ objects/immutable_map.go | 14 ++++++++++ objects/immutable_map_iterator.go | 14 ---------- objects/iterable.go | 7 +++++ objects/map.go | 14 ++++++++++ objects/map_iterator.go | 14 ---------- objects/string.go | 12 +++++++++ objects/string_iterator.go | 10 ------- runtime/vm.go | 21 ++++++--------- runtime/vm_iterable_test.go | 44 +++++++++++++++++++++++++++++++ 11 files changed, 107 insertions(+), 59 deletions(-) create mode 100644 objects/iterable.go create mode 100644 runtime/vm_iterable_test.go diff --git a/objects/array.go b/objects/array.go index d7109e6..e716709 100644 --- a/objects/array.go +++ b/objects/array.go @@ -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), + } +} diff --git a/objects/array_iterator.go b/objects/array_iterator.go index 69f7009..204faa4 100644 --- a/objects/array_iterator.go +++ b/objects/array_iterator.go @@ -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" diff --git a/objects/immutable_map.go b/objects/immutable_map.go index 70c46e7..60c60c1 100644 --- a/objects/immutable_map.go +++ b/objects/immutable_map.go @@ -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), + } +} diff --git a/objects/immutable_map_iterator.go b/objects/immutable_map_iterator.go index 4222616..9937706 100644 --- a/objects/immutable_map_iterator.go +++ b/objects/immutable_map_iterator.go @@ -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" diff --git a/objects/iterable.go b/objects/iterable.go new file mode 100644 index 0000000..e431d3d --- /dev/null +++ b/objects/iterable.go @@ -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 +} diff --git a/objects/map.go b/objects/map.go index 0bd800c..66dc0ad 100644 --- a/objects/map.go +++ b/objects/map.go @@ -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), + } +} diff --git a/objects/map_iterator.go b/objects/map_iterator.go index 17137dd..d60dd0e 100644 --- a/objects/map_iterator.go +++ b/objects/map_iterator.go @@ -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" diff --git a/objects/string.go b/objects/string.go index 1b6227c..f1c20e8 100644 --- a/objects/string.go +++ b/objects/string.go @@ -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), + } +} diff --git a/objects/string_iterator.go b/objects/string_iterator.go index 923ccf2..8bc95eb 100644 --- a/objects/string_iterator.go +++ b/objects/string_iterator.go @@ -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" diff --git a/runtime/vm.go b/runtime/vm.go index a15e8e5..d16288b 100644 --- a/runtime/vm.go +++ b/runtime/vm.go @@ -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 diff --git a/runtime/vm_iterable_test.go b/runtime/vm_iterable_test.go new file mode 100644 index 0000000..b7a43da --- /dev/null +++ b/runtime/vm_iterable_test.go @@ -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()}) +}