From 5585b496fb9e9ff54f732b7a178446546ae4260f Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Wed, 6 May 2026 04:04:08 +0200 Subject: [PATCH] iter-iter: changed item operation from function and args to a list of expressions --- iter-factory.go | 27 +++++++--------- iter-iter.go | 47 +++++++++++++-------------- operand-iterator.go | 77 ++++++++++++++++++--------------------------- operator-groupby.go | 7 +++-- operator-join.go | 13 +++++--- operator-map.go | 7 +++-- t_iter-iter_test.go | 7 +++-- 7 files changed, 86 insertions(+), 99 deletions(-) diff --git a/iter-factory.go b/iter-factory.go index 3d097b9..319d627 100644 --- a/iter-factory.go +++ b/iter-factory.go @@ -5,13 +5,13 @@ package expr import ( - "fmt" "slices" "git.portale-stac.it/go-pkg/expr/kern" + "git.portale-stac.it/go-pkg/expr/scan" ) -func NewIterator(ctx kern.ExprContext, value any, args map[string]any) (it kern.Iterator, err error) { +func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Iterator, err error) { if value == nil { return NewArrayIterator([]any{}), nil } @@ -24,24 +24,21 @@ func NewIterator(ctx kern.ExprContext, value any, args map[string]any) (it kern. case []any: it = NewArrayIterator(v) case kern.Iterator: - // it = v - var op kern.Functor - if len(args) >= 1 { - if opArg, ok := args["op"]; ok { - if op, ok = opArg.(kern.Functor); !ok { - err = fmt.Errorf("the 'op' argument must be a kern.Functor, got %T", opArg) - } - } - } - if err == nil { - it, err = NewIterIter(v, ctx, op, args) - } + // var exprs []*scan.Term + it, err = NewIterIter(v, ctx, ops) default: it = NewArrayIterator([]any{value}) } return } -func HasStandardOperation(name string) bool { +func HasIterStandardOperations(name string) bool { return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name) } + +func HasIterOperations(name string, ops ...string) bool { + return slices.Contains([]string{ + kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName, + }, name) || + slices.Contains(ops, name) +} diff --git a/iter-iter.go b/iter-iter.go index a4fa7ee..8b343ee 100644 --- a/iter-iter.go +++ b/iter-iter.go @@ -9,33 +9,20 @@ import ( "io" "git.portale-stac.it/go-pkg/expr/kern" + "git.portale-stac.it/go-pkg/expr/scan" ) type IterIter struct { - it kern.Iterator - count int64 - index int64 - ctx kern.ExprContext - op kern.Functor - opName string - args map[string]any - current any + it kern.Iterator + count int64 + index int64 + ctx kern.ExprContext + exprList []*scan.Term + current any } -func NewIterIter(it kern.Iterator, ctx kern.ExprContext, op kern.Functor, args map[string]any) (iter kern.Iterator, err error) { - var opName string - if op != nil { - if f := op.GetFunc(); f != nil { - opName = f.Name() - // } else { - // return nil, fmt.Errorf("invalid functor argument for iter-iter: expected kern.Functor, got %T", args["op"]) - } - } - if len(opName) == 0 { - opName = "anonymous" - } - iter = &IterIter{it: it, count: 0, index: -1, ctx: ctx, op: op, opName: opName, args: args} - +func NewIterIter(it kern.Iterator, ctx kern.ExprContext, exprs []*scan.Term) (iter kern.Iterator, err error) { + iter = &IterIter{it: it, count: 0, index: -1, ctx: ctx, exprList: exprs, current: nil} return } @@ -48,7 +35,7 @@ func (it *IterIter) TypeName() string { } func (it *IterIter) HasOperation(name string) bool { - return HasStandardOperation(name) + return HasIterStandardOperations(name) } func (it *IterIter) CallOperation(name string, args map[string]any) (v any, err error) { @@ -74,9 +61,17 @@ func (it *IterIter) CallOperation(name string, args map[string]any) (v any, err func (it *IterIter) Current() (item any, err error) { if it.current != nil { item = it.current - } else if it.op != nil { - if item, err = it.op.InvokeNamed(it.ctx, it.opName, it.args); err == nil { - it.current = item + } else if len(it.exprList) > 0 { + // Evaluate the expression list and use the result as the current item + var exprValue any + for _, expr := range it.exprList { + if exprValue, err = expr.Compute(it.ctx); err != nil { + break + } + it.ctx.UnsafeSetVar(kern.ControlLastResult, exprValue) + } + if err == nil { + item = exprValue } } else { var exists bool diff --git a/operand-iterator.go b/operand-iterator.go index b6156a5..b0b1555 100644 --- a/operand-iterator.go +++ b/operand-iterator.go @@ -5,9 +5,7 @@ package expr import ( - "fmt" "slices" - "strconv" "strings" "git.portale-stac.it/go-pkg/expr/kern" @@ -144,60 +142,45 @@ func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { 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 { - // if v, err = evalIterIter(ctx, firstChildValue, siblings); err == nil && v == nil { - // 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:] { - // args["arg"+strconv.Itoa(i+1)] = arg - // } - // v, err = NewIterIter(it, ctx, op, args) - // } else { - // err = opTerm.Children[1].Errorf("the first sibling parameter must be a functor to be used as operation for the iterator") - // } - // } else { - // v, err = NewIterIter(it, ctx, nil, nil) - // } - if v, err = evalIterIter(ctx, firstChildValue, siblings); err == nil && v == nil { - v = NewArrayIterator(siblings) - } + 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 +// 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 -} +// 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)) diff --git a/operator-groupby.go b/operator-groupby.go index 48c3180..70535a4 100644 --- a/operator-groupby.go +++ b/operator-groupby.go @@ -31,6 +31,7 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { var item any var sKey string var keyByIndex bool + var ok bool if err = opTerm.CheckOperands(); err != nil { return @@ -40,8 +41,10 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { return } - if it, err = NewIterator(ctx, leftValue, nil); err != nil { - return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue)) + if it, ok = leftValue.(kern.Iterator); !ok { + if it, err = NewIterator(ctx, leftValue, nil); err != nil { + return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue)) + } } rightTk := opTerm.Children[1].Tk diff --git a/operator-join.go b/operator-join.go index daeda13..66107dc 100644 --- a/operator-join.go +++ b/operator-join.go @@ -28,6 +28,7 @@ func evalJoin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { var leftValue, rightValue any var itLeft, itRight kern.Iterator var item any + var ok bool if err = opTerm.CheckOperands(); err != nil { return @@ -41,12 +42,16 @@ func evalJoin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { return } - if itLeft, err = NewIterator(ctx, leftValue, nil); err != nil { - return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", kern.TypeName(leftValue)) + if itLeft, ok = leftValue.(kern.Iterator); !ok { + if itLeft, err = NewIterator(ctx, leftValue, nil); err != nil { + return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", kern.TypeName(leftValue)) + } } - if itRight, err = NewIterator(ctx, rightValue, nil); err != nil { - return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", kern.TypeName(rightValue)) + if itRight, ok = rightValue.(kern.Iterator); !ok { + if itRight, err = NewIterator(ctx, rightValue, nil); err != nil { + return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", kern.TypeName(rightValue)) + } } values := kern.NewListA() diff --git a/operator-map.go b/operator-map.go index 60ff9ec..db7e275 100644 --- a/operator-map.go +++ b/operator-map.go @@ -28,6 +28,7 @@ func evalMap(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { var leftValue, rightValue any var it kern.Iterator var item any + var ok bool if err = opTerm.CheckOperands(); err != nil { return @@ -37,8 +38,10 @@ func evalMap(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { return } - if it, err = NewIterator(ctx, leftValue, nil); err != nil { - return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue)) + if it, ok = leftValue.(kern.Iterator); !ok { + if it, err = NewIterator(ctx, leftValue, nil); err != nil { + return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue)) + } } values := kern.NewListA() diff --git a/t_iter-iter_test.go b/t_iter-iter_test.go index 66565a2..a550ce0 100644 --- a/t_iter-iter_test.go +++ b/t_iter-iter_test.go @@ -14,11 +14,12 @@ func TestIterIterator(t *testing.T) { section := "Iterator" inputs := []inputType{ /* 1 */ {`it=$(4); $(it) filter ${_}==100`, kern.NewListA(), nil}, - /* 2 */ {`it=$(4); $(it, func(){$_}) filter ${_}==100`, kern.NewListA(), nil}, - /* 3 */ {`it=$(4); $(it, func(arg1){arg1+$_}, 10) filter ${_}==100`, kern.NewListA(), nil}, + /* 2 */ {`it=$(4); $(it, $_) filter ${_}==100`, kern.NewListA(), nil}, + /* 3 */ {`it=$(4); $(it, 10+$_, last-1) digest ${_}`, int64(12), nil}, + /* 4 */ {`f=func(n){last-n}; it=$(4); $(it, 10+$_, f(-1)) digest ${_}`, int64(14), nil}, } - runTestSuiteSpec(t, section, inputs, 3) + runTestSuiteSpec(t, section, inputs, 4) // runTestSuite(t, section, inputs) }