204 lines
5.3 KiB
Go
204 lines
5.3 KiB
Go
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
// All rights reserved.
|
|
|
|
// operand-iterator.go
|
|
package expr
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
|
|
"git.portale-stac.it/go-pkg/expr/kern"
|
|
"git.portale-stac.it/go-pkg/expr/scan"
|
|
)
|
|
|
|
// -------- iterator term
|
|
|
|
func newIteratorTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
|
tk.Sym = scan.SymIterator
|
|
return &scan.Term{
|
|
Tk: *tk,
|
|
Parent: nil,
|
|
Children: args,
|
|
Position: scan.PosLeaf,
|
|
Priority: scan.PriValue,
|
|
EvalFunc: evalIterator,
|
|
}
|
|
}
|
|
|
|
// -------- eval iterator
|
|
|
|
func evalTermArray(ctx kern.ExprContext, terms []*scan.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 kern.ExprContext, iteratorTerm *scan.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 *scan.Term, firstChildValue any) (ds map[string]kern.Functor, err error) {
|
|
if dictAny, ok := firstChildValue.(*kern.DictType); ok {
|
|
requiredFields := []string{kern.NextName}
|
|
fieldsMask := 0b1
|
|
foundFields := 0
|
|
ds = make(map[string]kern.Functor)
|
|
for keyAny, item := range *dictAny {
|
|
if key, ok := keyAny.(string); ok {
|
|
if functor, ok := item.(kern.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 = iteratorTerm.Children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|
var firstChildValue any
|
|
var ds map[string]kern.Functor
|
|
|
|
if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
|
|
return
|
|
}
|
|
|
|
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil && ds == nil {
|
|
return
|
|
}
|
|
err = nil
|
|
|
|
if ds != nil {
|
|
if len(ds) > 0 {
|
|
var dc *dataCursor
|
|
dcCtx := ctx.Clone()
|
|
if initFunc, exists := ds[kern.InitName]; exists && initFunc != nil {
|
|
var args []any
|
|
var resource any
|
|
if len(opTerm.Children) > 1 {
|
|
if args, err = evalTermArray(ctx, opTerm.Children[1:]); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
args = []any{}
|
|
}
|
|
|
|
actualParams := kern.BindActualParams(initFunc, args)
|
|
|
|
initCtx := ctx.Clone()
|
|
if resource, err = initFunc.InvokeNamed(initCtx, kern.InitName, actualParams); err != nil {
|
|
return
|
|
}
|
|
kern.ExportObjects(dcCtx, initCtx)
|
|
dc = NewDataCursor(dcCtx, ds, resource)
|
|
} else {
|
|
dc = NewDataCursor(dcCtx, ds, nil)
|
|
}
|
|
|
|
v = dc
|
|
} else {
|
|
if dictIt, ok := firstChildValue.(*kern.DictType); ok {
|
|
var args []any
|
|
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
|
v, err = NewDictIterator(dictIt, args)
|
|
}
|
|
} else {
|
|
err = opTerm.Children[0].Errorf("the data-source must be a dictionary")
|
|
}
|
|
}
|
|
} else if list, ok := firstChildValue.(*kern.ListType); ok {
|
|
var args []any
|
|
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
|
v = NewListIterator(list, args)
|
|
}
|
|
} else if intVal, ok := firstChildValue.(int64); ok {
|
|
var args []any
|
|
if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
|
|
v, err = NewIntIterator(args)
|
|
}
|
|
} else if it, ok := firstChildValue.(kern.Iterator); ok {
|
|
v, err = NewIterIter(it, ctx, opTerm.Children[1:])
|
|
} else {
|
|
var siblings []any
|
|
if siblings, err = evalSiblings(ctx, opTerm.Children, firstChildValue); err == nil {
|
|
v = NewArrayIterator(siblings)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// func evalIterIter(ctx kern.ExprContext, firstChildValue any, siblings []any) (v any, err error) {
|
|
// var op kern.Functor
|
|
// var args map[string]any
|
|
|
|
// if it, ok := firstChildValue.(kern.Iterator); ok {
|
|
// if len(siblings) > 1 {
|
|
// if op, ok = siblings[1].(kern.Functor); ok {
|
|
// args = make(map[string]any, len(siblings)-2)
|
|
// for i, arg := range siblings[2:] {
|
|
// switch a := arg.(type) {
|
|
// case *kern.DictType:
|
|
// for keyAny, item := range *a {
|
|
// if key, ok := keyAny.(string); ok {
|
|
// args[key] = item
|
|
// }
|
|
// }
|
|
// default:
|
|
// args["arg"+strconv.Itoa(i+1)] = arg
|
|
// }
|
|
// }
|
|
// } else if op == nil {
|
|
// return nil, fmt.Errorf("the first sibling parameter must be a functor to be used as operation for the iterator")
|
|
// }
|
|
// }
|
|
// v, err = NewIterIter(it, ctx, op, args)
|
|
// }
|
|
// return
|
|
// }
|
|
|
|
func evalSiblings(ctx kern.ExprContext, terms []*scan.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
|
|
}
|