From e5a61b56388d5ca9b415700c2b5a80e44fa08b32 Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Sat, 9 May 2026 12:15:59 +0200 Subject: [PATCH] iter-ops raised above assign; 'cat' operator returns an iterator; $$ operator supports iterators producing a list of items --- kern/iterator.go | 43 +++++++++++++++++++++ operator-cat.go | 94 +++++++++++++++++++++++++++++++++++++++++---- operator-context.go | 18 ++++++++- scan/symbol-map.go | 1 + scan/term.go | 2 +- t_iterator_test.go | 11 ++++++ 6 files changed, 159 insertions(+), 10 deletions(-) diff --git a/kern/iterator.go b/kern/iterator.go index 6667aa3..86f1185 100644 --- a/kern/iterator.go +++ b/kern/iterator.go @@ -7,6 +7,7 @@ package kern import ( // "errors" "fmt" + "slices" ) // Operator names @@ -50,3 +51,45 @@ func IsIterator(v any) (ok bool) { _, ok = v.(Iterator) return } + +type IteratorBase struct { + ItemIndex int64 + ItemCount int64 + current any +} + +func (it *IteratorBase) Current() (item any, err error) { + return it.current, nil +} + +func (it *IteratorBase) Index() int64 { + return it.ItemIndex +} + +func (it *IteratorBase) Count() int64 { + return it.ItemCount +} + +func (it *IteratorBase) HasOperation(name string) bool { + return slices.Contains([]string{NextName, IndexName, CountName, CurrentName}, name) +} + +func (it *IteratorBase) Clean() (err error) { + return +} + +func (it *IteratorBase) Reset() (err error) { + it.ItemIndex = -1 + it.ItemCount = 0 + it.current = nil + return +} + +func (it *IteratorBase) Increment() { + it.ItemIndex++ + it.ItemCount++ +} + +func (it *IteratorBase) SetCurrent(v any) { + it.current = v +} diff --git a/operator-cat.go b/operator-cat.go index 25ea85a..b3f1185 100644 --- a/operator-cat.go +++ b/operator-cat.go @@ -27,7 +27,7 @@ func newCatTerm(tk *scan.Token) (inst *scan.Term) { func evalCat(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { var leftValue, rightValue any var itLeft, itRight kern.Iterator - var item any + // var item any var ok bool if err = opTerm.CheckOperands(); err != nil { @@ -54,16 +54,94 @@ func evalCat(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { } } - values := kern.NewListA() - for _, it := range []kern.Iterator{itLeft, itRight} { - for item, err = it.Next(); err == nil; item, err = it.Next() { - values.AppendItem(item) + // values := kern.NewListA() + // for _, it := range []kern.Iterator{itLeft, itRight} { + // for item, err = it.Next(); err == nil; item, err = it.Next() { + // values.AppendItem(item) + // } + // } + // if err == io.EOF { + // err = nil + // } + // v = values + v = newCatIterator(itLeft, itRight) + return +} + +const catIteratorType = "cat" + +type catIterator struct { + kern.IteratorBase + itLeft kern.Iterator + itRight kern.Iterator + itCurrent kern.Iterator +} + +func newCatIterator(itLeft, itRight kern.Iterator) (it *catIterator) { + it = &catIterator{ + IteratorBase: kern.IteratorBase{}, + itLeft: itLeft, + itRight: itRight, + itCurrent: itLeft, + } + it.Reset() + return +} + +func (it *catIterator) TypeName() string { + return catIteratorType +} + +func (it *catIterator) String() string { + return fmt.Sprintf("$(%s %s %s)", it.itLeft, catIteratorType, it.itRight) +} + +func (it *catIterator) Next() (item any, err error) { + if it.itCurrent == nil { + err = io.EOF + } else { + if item, err = it.itCurrent.Next(); err == nil { + it.Increment() + } else if err == io.EOF { + if it.itCurrent == it.itLeft { + it.itCurrent = it.itRight + } else { + return + } + if item, err = it.itCurrent.Next(); err == nil { + it.Increment() + } else { + it.itCurrent = nil + } } } - if err == io.EOF { - err = nil + it.SetCurrent(item) + return +} + +// func (it *catIterator) Reset() (err error) { +// err = it.itLeft.Reset() +// it.IteratorBase.Reset() +// return +// } + +func (it *catIterator) CallOperation(name string, args map[string]any) (v any, err error) { + switch name { + case kern.NextName: + v, err = it.Next() + // case kern.ResetName: + // err = it.Reset() + // case kern.CleanName: + // err = it.Clean() + case kern.IndexName: + v = it.Index() + case kern.CurrentName: + v, err = it.Current() + case kern.CountName: + v = it.Count() + default: + err = kern.ErrNoOperation(name) } - v = values return } diff --git a/operator-context.go b/operator-context.go index bc13085..fa3086c 100644 --- a/operator-context.go +++ b/operator-context.go @@ -5,6 +5,8 @@ package expr import ( + "io" + "git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/scan" ) @@ -23,8 +25,8 @@ func newContextTerm(tk *scan.Token) (inst *scan.Term) { func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { var childValue any - var sourceCtx kern.ExprContext + if len(opTerm.Children) == 0 { sourceCtx = ctx } else if opTerm.Children[0].Symbol() == scan.SymVariable && opTerm.Children[0].Source() == "global" { @@ -53,6 +55,20 @@ func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error } v = d } + } else if childValue != nil { + if it, ok := childValue.(kern.Iterator); ok { + var item any + values := kern.NewListA() + for item, err = it.Next(); err == nil; item, err = it.Next() { + values.AppendItem(item) + } + if err == io.EOF { + err = nil + v = values + } + } else { + err = opTerm.ErrIncompatiblePrefixPostfixType(childValue) + } } else { err = opTerm.ErrIncompatiblePrefixPostfixType(childValue) } diff --git a/scan/symbol-map.go b/scan/symbol-map.go index 9d55d51..b0e3efb 100644 --- a/scan/symbol-map.go +++ b/scan/symbol-map.go @@ -138,6 +138,7 @@ func init() { SymKwMap: {"map", SymClassOperator, PosInfix}, SymKwFilter: {"filter", SymClassOperator, PosInfix}, SymKwDigest: {"digest", SymClassOperator, PosInfix}, + SymKwCat: {"cat", SymClassOperator, PosInfix}, SymKwJoin: {"join", SymClassOperator, PosInfix}, SymKwGroupBy: {"groupby", SymClassOperator, PosInfix}, SymKwFunc: {"func(", SymClassDeclaration, PosPrefix}, diff --git a/scan/term.go b/scan/term.go index 13c4917..80af3a6 100644 --- a/scan/term.go +++ b/scan/term.go @@ -15,9 +15,9 @@ type TermPriority uint32 const ( PriNone TermPriority = iota PriRange - PriIterOp // map, filter, digest, etc PriBut PriAssign + PriIterOp // map, filter, digest, etc PriInsert PriOr PriAnd diff --git a/t_iterator_test.go b/t_iterator_test.go index d3a55ef..660bb85 100644 --- a/t_iterator_test.go +++ b/t_iterator_test.go @@ -49,3 +49,14 @@ func TestIteratorParser(t *testing.T) { // runTestSuiteSpec(t, section, inputs, 10) runTestSuite(t, section, inputs) } + +func TestCatIterator(t *testing.T) { + section := "Iterator" + inputs := []inputType{ + /* 1 */ {`([1] cat [2]) map $_`, kern.NewList([]any{int64(1), int64(2)}), nil}, + /* 2 */ {`$$([1] cat [2])`, kern.NewList([]any{int64(1), int64(2)}), nil}, + } + + // runTestSuiteSpec(t, section, inputs, 2) + runTestSuite(t, section, inputs) +}