new operator 'join'

This commit is contained in:
Celestino Amoroso 2026-04-25 06:55:09 +02:00
parent 49728307f3
commit 7d2cf1e687
6 changed files with 74 additions and 4 deletions

View File

@ -9,7 +9,7 @@ import (
"io" "io"
) )
//-------- map term //-------- digest term
func newDigestTerm(tk *Token) (inst *term) { func newDigestTerm(tk *Token) (inst *term) {
return &term{ return &term{
@ -35,7 +35,7 @@ func evalDigest(ctx ExprContext, opTerm *term) (v any, err error) {
} }
if it, err = NewIterator(leftValue); err != nil { if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", TypeName(leftValue)) return nil, fmt.Errorf("left operand of DIGEST must be an iterable data-source; got %s", TypeName(leftValue))
} }
lastValue = nil lastValue = nil

View File

@ -35,7 +35,7 @@ func evalFilter(ctx ExprContext, opTerm *term) (v any, err error) {
} }
if it, err = NewIterator(leftValue); err != nil { if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", TypeName(leftValue)) return nil, fmt.Errorf("left operand of FILTER must be an iterable data-source; got %s", TypeName(leftValue))
} }
values := newListA() values := newListA()

65
operator-join.go Normal file
View File

@ -0,0 +1,65 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-join.go
package expr
import (
"fmt"
"io"
)
//-------- join term
func newJoinTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalJoin,
}
}
func evalJoin(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var itLeft, itRight Iterator
var item any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return
}
if rightValue, err = opTerm.children[1].compute(ctx); err != nil {
return
}
if itLeft, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", TypeName(leftValue))
}
if itRight, err = NewIterator(rightValue); err != nil {
return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", TypeName(rightValue))
}
values := newListA()
for _, it := range []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
return
}
// init
func init() {
registerTermConstructor(SymKwJoin, newJoinTerm)
}

View File

@ -138,6 +138,7 @@ func init() {
SymKwMap: {"map", symClassOperator, posInfix}, SymKwMap: {"map", symClassOperator, posInfix},
SymKwFilter: {"filter", symClassOperator, posInfix}, SymKwFilter: {"filter", symClassOperator, posInfix},
SymKwDigest: {"digest", symClassOperator, posInfix}, SymKwDigest: {"digest", symClassOperator, posInfix},
SymKwJoin: {"join", symClassOperator, posInfix},
SymKwFunc: {"func(", symClassDeclaration, posPrefix}, SymKwFunc: {"func(", symClassDeclaration, posPrefix},
SymKwBuiltin: {"builtin", symClassOperator, posPrefix}, SymKwBuiltin: {"builtin", symClassOperator, posPrefix},
SymKwPlugin: {"plugin", symClassOperator, posPrefix}, SymKwPlugin: {"plugin", symClassOperator, posPrefix},

View File

@ -122,6 +122,7 @@ const (
SymKwMap SymKwMap
SymKwFilter SymKwFilter
SymKwDigest SymKwDigest
SymKwJoin
SymKwNil SymKwNil
SymKwUnset SymKwUnset
) )
@ -145,5 +146,6 @@ func init() {
"NIL": SymKwNil, "NIL": SymKwNil,
"UNSET": SymKwUnset, "UNSET": SymKwUnset,
"DIGEST": SymKwDigest, "DIGEST": SymKwDigest,
"JOIN": SymKwJoin,
} }
} }

View File

@ -37,10 +37,12 @@ func TestOperator(t *testing.T) {
/* 24 */ {`[1,2,3] map $_`, newList([]any{int64(1), int64(2), int64(3)}), nil}, /* 24 */ {`[1,2,3] map $_`, newList([]any{int64(1), int64(2), int64(3)}), nil},
/* 25 */ {`[1,2,3,4] filter ($_ % 2 == 0)`, newList([]any{int64(2), int64(4)}), nil}, /* 25 */ {`[1,2,3,4] filter ($_ % 2 == 0)`, newList([]any{int64(2), int64(4)}), nil},
/* 26 */ {`max=0; [2,3,1] digest max=(($_ > max) ? {$_} :: {max})`, int64(3), nil}, /* 26 */ {`max=0; [2,3,1] digest max=(($_ > max) ? {$_} :: {max})`, int64(3), nil},
/* 27 */ {`["a","b"] join ["x"]`, newList([]any{"a", "b", "x"}), nil},
/* 28 */ {`["a","b"] join ["x"-true]`, nil, `[1:21] left operand 'x' [string] and right operand 'true' [bool] are not compatible with operator "-"`},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 25) // runTestSuiteSpec(t, section, inputs, 28)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }