// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // operand-iterator.go package expr import ( "fmt" "slices" "strings" ) // -------- iterator term func newIteratorTerm(tk *Token, dsTerm *term, args []*term) *term { tk.Sym = SymIterator children := make([]*term, 0, 1+len(args)) children = append(children, dsTerm) children = append(children, args...) return &term{ tk: *tk, parent: nil, children: children, position: posLeaf, priority: priValue, evalFunc: evalIterator, } } // -------- eval iterator func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) { values = make([]any, len(a)) for i, t := range a { var value any if value, err = t.compute(ctx); err == nil { values[i] = value } else { break } } 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) { 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 } requiredFields := []string{currentName, nextName} fieldsMask := 0b11 foundFields := 0 if dictAny, ok := value.(map[any]any); ok { ds = make(map[string]Functor) for keyAny, item := range dictAny { if key, ok := keyAny.(string); ok { if functor, ok := item.(*funcDefFunctor); ok { ds[key] = functor if index := slices.Index(requiredFields, key); index >= 0 { foundFields |= 1 << index } } } } // check required functions if foundFields != fieldsMask { missingFields := make([]string, 0, len(requiredFields)) for index, field := range requiredFields { 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 { err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value) } return } func evalIterator(ctx ExprContext, self *term) (v any, err error) { var ds map[string]Functor if ds, err = getDataSourceDict(ctx, self); err != nil { return } dc := newDataCursor(ctx, ds) if initFunc, exists := ds[initName]; exists && initFunc != nil { var args []any if len(self.children) > 1 { if args, err = evalTermArray(ctx, self.children[1:]); err != nil { return } } else { args = []any{} } initCtx := dc.ctx.Clone() if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil { return } exportObjects(dc.ctx, initCtx) } dc.nextFunc, _ = ds[nextName] dc.currentFunc, _ = ds[currentName] dc.cleanFunc, _ = ds[cleanName] dc.resetFunc, _ = ds[resetName] v = dc return }