Iterators now support generic operation interface based on methods HasOperation() and CallOperation().

Also fixed mul() function that called doAdd() instead of doMul().
This commit is contained in:
Celestino Amoroso 2024-04-28 06:45:20 +02:00
parent fc0e1ffaee
commit aa66d07caa
5 changed files with 100 additions and 16 deletions

View File

@ -18,7 +18,7 @@ const (
) )
type dataCursor struct { type dataCursor struct {
ds map[any]*term ds map[string]Functor
ctx ExprContext ctx ExprContext
index int index int
resource any resource any
@ -28,8 +28,9 @@ type dataCursor struct {
currentFunc Functor currentFunc Functor
} }
func newDataCursor(ctx ExprContext) (dc *dataCursor) { func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
dc = &dataCursor{ dc = &dataCursor{
ds: ds,
index: -1, index: -1,
ctx: ctx.Clone(), ctx: ctx.Clone(),
} }
@ -40,6 +41,24 @@ func (dc *dataCursor) String() string {
return "$(...)" return "$(...)"
} }
func (dc *dataCursor) HasOperation(name string) (exists bool) {
f, ok := dc.ds[name]
exists = ok && isFunctor(f)
return
}
func (dc *dataCursor) CallOperation(name string) (value any, err error) {
if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
ctx := cloneContext(dc.ctx)
value, err = functor.Invoke(ctx, name, []any{})
exportObjects(dc.ctx, ctx)
} else {
err = errNoOperation(name)
}
return
}
func (dc *dataCursor) Reset() (err error) { func (dc *dataCursor) Reset() (err error) {
if dc.resetFunc != nil { if dc.resetFunc != nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)

View File

@ -27,6 +27,11 @@ func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) {
if v, err = doAdd(ctx, name, subIter); err != nil { if v, err = doAdd(ctx, name, subIter); err != nil {
break break
} }
if subIter.HasOperation(cleanName) {
if _, err = subIter.CallOperation(cleanName, nil); err != nil {
return
}
}
} else { } else {
if err = checkNumberParamExpected(name, v, it.Index()); err != nil { if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
break break
@ -77,7 +82,7 @@ func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) {
} }
if array, ok := v.([]any); ok { if array, ok := v.([]any); ok {
if v, err = doAdd(ctx, name, NewFlatArrayIterator(array)); err != nil { if v, err = doMul(ctx, name, NewFlatArrayIterator(array)); err != nil {
break break
} }
} }

View File

@ -15,6 +15,14 @@ func NewFlatArrayIterator(array []any) *FlatArrayIterator {
return &FlatArrayIterator{a: array, index: 0} return &FlatArrayIterator{a: array, index: 0}
} }
func (it *FlatArrayIterator) HasOperation(name string) bool {
return false
}
func (it *FlatArrayIterator) CallOperation(name string, args []any) (any, error) {
return nil, errNoOperation(name)
}
func (it *FlatArrayIterator) Current() (item any, err error) { func (it *FlatArrayIterator) Current() (item any, err error) {
if it.index >= 0 && it.index < len(it.a) { if it.index >= 0 && it.index < len(it.a) {
item = it.a[it.index] item = it.a[it.index]

View File

@ -4,8 +4,16 @@
// iterator.go // iterator.go
package expr package expr
import "fmt"
type Iterator interface { type Iterator interface {
Next() (item any, err error) // must return io.EOF after the last item Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error) Current() (item any, err error)
Index() int Index() int
HasOperation(name string) bool
CallOperation(name string, args []any) (value any, err error)
}
func errNoOperation(name string) error {
return fmt.Errorf("no %q function defined in the data-source", name)
} }

View File

@ -6,6 +6,8 @@ package expr
import ( import (
"fmt" "fmt"
"slices"
"strings"
) )
// -------- iterator term // -------- iterator term
@ -41,6 +43,44 @@ func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
return return
} }
// func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err error) {
// var value any
// if len(self.children) < 1 || self.children[0] == nil {
// err = self.Errorf("missing the data-source parameter")
// return
// }
// if value, err = self.children[0].compute(ctx); err != nil {
// return
// }
// if dictAny, ok := value.(map[any]any); ok {
// ds = make(map[string]Functor)
// // required functions
// for _, k := range []string{currentName, nextName} {
// if item, exists := dictAny[k]; exists && item != nil {
// if functor, ok := item.(*funcDefFunctor); ok {
// ds[k] = functor
// }
// } else {
// err = fmt.Errorf("the data-source must provide a non-nil %q operator", k)
// return
// }
// }
// // Optional functions
// for _, k := range []string{initName, resetName, cleanName} {
// if item, exists := dictAny[k]; exists && item != nil {
// if functor, ok := item.(*funcDefFunctor); ok {
// ds[k] = functor
// }
// }
// }
// } else {
// err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value)
// }
// return
// }
func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err error) { func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err error) {
var value any var value any
if len(self.children) < 1 || self.children[0] == nil { if len(self.children) < 1 || self.children[0] == nil {
@ -52,26 +92,30 @@ func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err
return return
} }
requiredFields := []string{currentName, nextName}
fieldsMask := 0b11
foundFields := 0
if dictAny, ok := value.(map[any]any); ok { if dictAny, ok := value.(map[any]any); ok {
ds = make(map[string]Functor) ds = make(map[string]Functor)
// required functions for keyAny, item := range dictAny {
for _, k := range []string{currentName, nextName} { if key, ok := keyAny.(string); ok {
if item, exists := dictAny[k]; exists && item != nil {
if functor, ok := item.(*funcDefFunctor); ok { if functor, ok := item.(*funcDefFunctor); ok {
ds[k] = functor ds[key] = functor
if index := slices.Index(requiredFields, key); index >= 0 {
foundFields |= 1 << index
}
} }
} else {
err = fmt.Errorf("the data-source must provide a non-nil %q operator", k)
return
} }
} }
// Optional functions // check required functions
for _, k := range []string{initName, resetName, cleanName} { if foundFields != fieldsMask {
if item, exists := dictAny[k]; exists && item != nil { missingFields := make([]string, 0, len(requiredFields))
if functor, ok := item.(*funcDefFunctor); ok { for index, field := range requiredFields {
ds[k] = functor if (foundFields & (1 << index)) == 0 {
missingFields = append(missingFields, field)
} }
} }
err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
} }
} else { } else {
err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value) err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value)
@ -86,7 +130,7 @@ func evalIterator(ctx ExprContext, self *term) (v any, err error) {
return return
} }
dc := newDataCursor(ctx) dc := newDataCursor(ctx, ds)
if initFunc, exists := ds[initName]; exists && initFunc != nil { if initFunc, exists := ds[initName]; exists && initFunc != nil {
var args []any var args []any