// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // operand-iterator.go package expr import ( "slices" "strings" ) // -------- iterator term // func newDsIteratorTerm(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, // } // } func newIteratorTerm(tk *Token, args []*term) *term { tk.Sym = SymIterator return &term{ tk: *tk, parent: nil, children: args, position: posLeaf, priority: priValue, evalFunc: evalIterator, } } // -------- eval iterator func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) { values = make([]any, len(terms)) for i, t := range terms { var value any if value, err = t.compute(ctx); err == nil { values[i] = value } else { break } } return } func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error) { if len(iteratorTerm.children) < 1 || iteratorTerm.children[0] == nil { err = iteratorTerm.Errorf("missing the data-source parameter") return } value, err = iteratorTerm.children[0].compute(ctx) return } func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) { if dictAny, ok := firstChildValue.(*DictType); ok { requiredFields := []string{currentName, nextName} fieldsMask := 0b11 foundFields := 0 ds = make(map[string]Functor) for keyAny, item := range *dictAny { if key, ok := keyAny.(string); ok { if functor, ok := item.(Functor); 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, ", ")) err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", ")) } } return } func evalIterator(ctx ExprContext, iteratorTerm *term) (v any, err error) { var firstChildValue any var ds map[string]Functor if firstChildValue, err = evalFirstChild(ctx, iteratorTerm); err != nil { return } if ds, err = getDataSourceDict(iteratorTerm, firstChildValue); err != nil { return } if ds != nil { dc := newDataCursor(ctx, ds) if initFunc, exists := ds[initName]; exists && initFunc != nil { var args []any if len(iteratorTerm.children) > 1 { if args, err = evalTermArray(ctx, iteratorTerm.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 } else if list, ok := firstChildValue.(*ListType); ok { var args []any if args, err = evalSibling(ctx, iteratorTerm.children, nil); err == nil { v = NewListIterator(list, args) } } else { var list []any if list, err = evalSibling(ctx, iteratorTerm.children, firstChildValue); err == nil { v = NewArrayIterator(list) } } return } func evalSibling(ctx ExprContext, terms []*term, firstChildValue any) (list []any, err error) { items := make([]any, 0, len(terms)) for i, tree := range terms { var param any if i == 0 { if firstChildValue == nil { continue } param = firstChildValue } else if param, err = tree.compute(ctx); err != nil { break } items = append(items, param) } if err == nil { list = items } return }