int-iterator is now defined interval

This commit is contained in:
2026-07-28 07:16:49 +02:00
parent 8e596d5979
commit 648533cbe3
12 changed files with 90 additions and 70 deletions
+11
View File
@@ -11,6 +11,7 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types" "git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/interval"
) )
type IntIterator struct { type IntIterator struct {
@@ -21,6 +22,16 @@ type IntIterator struct {
step int64 step int64
} }
func NewIntIteratorFromInterval(p *interval.IntervalType) (it *IntIterator, err error) {
b := p.Begin()
e := p.End()
s := p.Step()
if b > e {
s = -s
}
return NewIntIteratorA(b, e, s)
}
func NewIntIteratorA(args ...any) (it *IntIterator, err error) { func NewIntIteratorA(args ...any) (it *IntIterator, err error) {
return NewIntIterator(args) return NewIntIterator(args)
} }
+8 -5
View File
@@ -13,6 +13,7 @@ import (
"git.portale-stac.it/go-pkg/expr/sym" "git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array" "git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict" "git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/interval"
"git.portale-stac.it/go-pkg/expr/types/list" "git.portale-stac.it/go-pkg/expr/types/list"
) )
@@ -146,11 +147,13 @@ func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil { if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
v = NewLinkedListIterator(ll, args) v = NewLinkedListIterator(ll, args)
} }
} else if intVal, ok := firstChildValue.(int64); ok { } else if intervalVal, ok := firstChildValue.(*interval.IntervalType); ok {
var args []any v, err = NewIntIteratorFromInterval(intervalVal)
if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil { // } else if intVal, ok := firstChildValue.(int64); ok {
v, err = NewIntIterator(args) // 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 { } else if it, ok := firstChildValue.(kern.Iterator); ok {
v, err = NewIterIter(it, ctx, opTerm.Children[1:]) v, err = NewIterIter(it, ctx, opTerm.Children[1:])
} else { } else {
+20 -18
View File
@@ -5,9 +5,6 @@
package expr package expr
import ( import (
"fmt"
"strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym" "git.portale-stac.it/go-pkg/expr/sym"
@@ -20,17 +17,17 @@ type selectorCase struct {
caseExpr kern.Expr caseExpr kern.Expr
} }
func (sc *selectorCase) String() string { // func (sc *selectorCase) String() string {
var sb strings.Builder // var sb strings.Builder
if sc.filterList != nil { // if sc.filterList != nil {
sc.filterList.ToString(&sb) // sc.filterList.ToString(&sb)
sb.WriteByte(' ') // sb.WriteByte(' ')
} // }
sb.WriteByte('{') // sb.WriteByte('{')
sb.WriteString(sc.caseExpr.String()) // sb.WriteString(sc.caseExpr.String())
sb.WriteByte('}') // sb.WriteByte('}')
return sb.String() // return sb.String()
} // }
func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term { func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term {
tk := scan.NewValueToken(row, col, sym.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr}) tk := scan.NewValueToken(row, col, sym.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
@@ -46,9 +43,14 @@ func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr
// -------- eval selector case // -------- eval selector case
func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var ok bool v, _ = opTerm.Value().(*selectorCase)
if v, ok = opTerm.Value().(*selectorCase); !ok {
err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
}
return return
} }
// func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// var ok bool
// if v, ok = opTerm.Value().(*selectorCase); !ok {
// err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
// }
// return
// }
+3 -3
View File
@@ -88,9 +88,9 @@ func checkIntervalComponent(t kern.Term, value any) (component int64, err error)
return return
} }
func errIntervalInvalidSpecification(t kern.Term) error { // func errIntervalInvalidSpecification(t kern.Term) error {
return t.Errorf("invalid %s specification", interval.TypeName) // return t.Errorf("invalid %s specification", interval.TypeName)
} // }
func errIntervalUnexpectedExpression(t *scan.Term) error { func errIntervalUnexpectedExpression(t *scan.Term) error {
return t.Errorf("unexpected %s expression", interval.TypeName) return t.Errorf("unexpected %s expression", interval.TypeName)
+16 -21
View File
@@ -173,33 +173,28 @@ func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSy
itemExpected := false itemExpected := false
itemCtx := remFlags(ctx, allowIndex) itemCtx := remFlags(ctx, allowIndex)
for lastSym != termSym && lastSym != sym.SymEos { for lastSym != termSym && lastSym != sym.SymEos {
// zeroRequired := scanner.Current().Sym == sym.SymDoubleDot
var itemTree *scan.Ast var itemTree *scan.Ast
if itemTree, err = parser.parseItem(scanner, itemCtx, sym.SymComma, termSym); err == nil { if itemTree, err = parser.parseItem(scanner, itemCtx, sym.SymComma, termSym); err == nil {
root := itemTree.Root() root := itemTree.Root()
if root != nil { if root != nil {
if hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymDoubleDot { if root.Symbol() == sym.SymDoubleDot {
changeColonToInterval(root) if hasFlag(ctx, allowIndex) {
changeColonToInterval(root)
} else {
// err = root.Errorf("unexpected range expression")
err = errIntervalUnexpectedExpression(root)
break
}
} }
if !hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymInterval { // if hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymDoubleDot {
// err = root.Errorf("unexpected range expression") // changeColonToInterval(root)
err = errIntervalUnexpectedExpression(root)
break
}
args = append(args, root)
// if hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymInterval && zeroRequired { //len(root.children) == 0 {
// if len(root.Children) == 1 {
// root.Children = append(root.Children, root.Children[0])
// } else if len(root.Children) > 1 {
// // err = root.Errorf("invalid range specification")
// err = errIntervalInvalidSpecification(root)
// break
// }
// zeroTk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), sym.SymInteger, "0", int64(0))
// zeroTerm := scan.NewTerm(zeroTk)
// zeroTerm.SetParent(root)
// root.Children[0] = zeroTerm
// } // }
// if !hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymInterval {
// // err = root.Errorf("unexpected range expression")
// err = errIntervalUnexpectedExpression(root)
// break
// }
args = append(args, root)
} else if itemExpected { } else if itemExpected {
prev := scanner.Previous() prev := scanner.Previous()
err = prev.ErrorExpectedGot("list-item") err = prev.ErrorExpectedGot("list-item")
+2 -2
View File
@@ -14,7 +14,7 @@ func TestFuncRun(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil}, /* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil},
/* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil}, /* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil},
/* 3 */ {`builtin "iterator"; run($(4), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil}, /* 3 */ {`builtin "iterator"; run($(..4), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil},
/* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil}, /* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil},
/* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`}, /* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, nil}, /* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, nil},
@@ -26,6 +26,6 @@ func TestFuncRun(t *testing.T) {
//t.Setenv("EXPR_PATH", ".") //t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 3) // RunTestSuiteSpec(t, section, inputs, 3)
RunTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+1 -1
View File
@@ -131,7 +131,7 @@ func doTest(t *testing.T, ctx kern.ExprContext, section string, input *inputType
good = false good = false
} }
} else { } else {
t.Errorf(">>>%s/%d: `%s` -> got-err = <nil>, expected-err = %v [%s]", section, count, input.source, input.wantResult, kern.TypeName(input.wantResult)) t.Errorf(">>>%s/%d: `%s` -> result = %v [%s], got-err = <nil>, expected-err = %v", section, count, input.source, gotResult, kern.TypeName(gotResult), wantErr)
good = false good = false
} }
} }
+1 -1
View File
@@ -83,7 +83,7 @@ func TestList(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`$$(5)`, list.NewLinkedListA(5), nil}, /* 1 */ {`$$(5)`, list.NewLinkedListA(5), nil},
/* 2 */ {`$$($(2))`, list.NewLinkedListA(0, 1), nil}, /* 2 */ {`$$($(..2))`, list.NewLinkedListA(0, 1), nil},
/* 3 */ {`string(($$global).funcs.bool)`, `bool(value):boolean{}`, nil}, /* 3 */ {`string(($$global).funcs.bool)`, `bool(value):boolean{}`, nil},
} }
+7 -5
View File
@@ -31,8 +31,12 @@ func TestExpr(t *testing.T) {
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`}, /* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil}, /* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil}, /* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
/* 18 */ {`$$`, dict.NewDict(map[any]any{"vars": dict.NewDict(nil), "funcs": dict.NewDict(nil)}), nil}, /* 18 */ {`2 ? {"a"}`, nil, `[1:2] no case catches the value (2) of the selection expression`},
/* 19 */ {` /* 19 */ {`2 ? 5`, nil, "[1:6] expected `{`, got `5`"},
/* 20 */ {`a=2; ${a}`, int64(2), nil},
/* 21 */ {`$_=2; $_`, int64(2), nil},
/* 22 */ {`$$`, dict.NewDict(map[any]any{"vars": dict.NewDict(nil), "funcs": dict.NewDict(nil)}), nil},
/* 23 */ {`
ds={ ds={
"init":func(@end){@current=0 but true}, "init":func(@end){@current=0 but true},
//"current":func(){current}, //"current":func(){current},
@@ -44,11 +48,9 @@ func TestExpr(t *testing.T) {
it++; it++;
it++ it++
`, int64(1), nil}, `, int64(1), nil},
/* 20 */ {`a=2; ${a}`, int64(2), nil},
/* 21 */ {`$_=2; $_`, int64(2), nil},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 21) // RunTestSuiteSpec(t, section, inputs, 19)
RunTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+5 -1
View File
@@ -41,10 +41,14 @@ func TestCollections(t *testing.T) {
/* 25 */ {`[<0,1,2,3,4>][0..]`, list.NewLinkedListA(0, 1, 2, 3, 4), nil}, /* 25 */ {`[<0,1,2,3,4>][0..]`, list.NewLinkedListA(0, 1, 2, 3, 4), nil},
/* 26 */ {`[<0,1,2,3,4>][0..-1..2]`, list.NewLinkedListA(0, 2), nil}, /* 26 */ {`[<0,1,2,3,4>][0..-1..2]`, list.NewLinkedListA(0, 2), nil},
/* 27 */ {`[<0,1,2,3,4>][-1..0..2]`, list.NewLinkedListA(4, 2), nil}, /* 27 */ {`[<0,1,2,3,4>][-1..0..2]`, list.NewLinkedListA(4, 2), nil},
/* 28 */ {`[-1..0..2]`, nil, `[1:8] unexpected interval expression`},
/* 29 */ {`"abcdef"['x'..]`, nil, `[1:14] interval expression expected integer, got string (x)`},
/* 30 */ {`"abcdef"[1+..]`, nil, `[1:12] infix operator "+" requires two non-nil operands, got 1`},
/* 31 */ {`"abcdef"[1..4+]`, nil, `[1:15] infix operator "+" requires two non-nil operands, got 1`},
} }
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
// RunTestSuiteSpec(t, section, inputs, 27) // RunTestSuiteSpec(t, section, inputs, 30)
RunTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+4 -4
View File
@@ -13,10 +13,10 @@ import (
func TestIterIterator(t *testing.T) { func TestIterIterator(t *testing.T) {
section := "Iter-Iter" section := "Iter-Iter"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`it=$(4); $$($(it) filter ${_}==100)`, list.NewLinkedListA(), nil}, /* 1 */ {`it=$(..4); $$($(it) filter ${_}==100)`, list.NewLinkedListA(), nil},
/* 2 */ {`it=$(4); $$($(it, $_) filter ${_}==100)`, list.NewLinkedListA(), nil}, /* 2 */ {`it=$(..4); $$($(it, $_) filter ${_}==100)`, list.NewLinkedListA(), nil},
/* 3 */ {`it=$(4); $(it, 10+$_, last-1) digest ${_}`, int64(12), 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}, /* 4 */ {`f=func(n){last-n}; it=$(..4); $(it, 10+$_, f(-1)) digest ${_}`, int64(14), nil},
} }
// runTestSuiteSpec(t, section, inputs, 4) // runTestSuiteSpec(t, section, inputs, 4)
+12 -9
View File
@@ -39,12 +39,15 @@ func TestIteratorParser(t *testing.T) {
/* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil}, /* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
/* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil}, /* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
/* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, array.NewArray([]any{int64(1), "one"}), nil}, /* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, array.NewArray([]any{int64(1), "one"}), nil},
/* 23 */ {`$$($(1,4,0))`, nil, `step cannot be zero`}, /* 23 */ {`$$($(1..4..0))`, nil, `[1:11] invalid interval specification: step must be positive`},
/* 24 */ {`$$($(1,4,-1))`, nil, `step cannot be negative when start < stop`}, /* 24 */ {`$$($(1..4..-1))`, nil, `[1:11] invalid interval specification: step must be positive`},
/* 25 */ {`$$($(4,1,1))`, nil, `step cannot be positive when start > stop`}, /* 25 */ {`$$($(4..1..1))`, list.NewLinkedListA(4, 3, 2), nil},
/* 26 */ {`it=$(1..5..2); it++`, int64(1), nil},
/* 27 */ {`it=$(1.5..5..2); it++`, nil, `[1:13] interval expression expected integer, got float (1.5)`},
/* 28 */ {`it=$(.."z"); it++`, nil, `[1:7] interval expression expected integer, got string (z)`},
} }
// runTestSuiteSpec(t, section, inputs, 25) // RunTestSuiteSpec(t, section, inputs, 27)
RunTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
@@ -138,9 +141,9 @@ func TestFilterIterator(t *testing.T) {
func TestDigestIterator(t *testing.T) { func TestDigestIterator(t *testing.T) {
section := "Iterator-Digest" section := "Iterator-Digest"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`$(10) digest ${_}`, int64(9), nil}, /* 1 */ {`$(..10) digest ${_}`, int64(9), nil},
/* 2 */ {`$(10,0) digest ${_}`, int64(1), nil}, /* 2 */ {`$(10..0) digest ${_}`, int64(1), nil},
/* 3 */ {`$(10,0,-2) digest ${_}`, int64(2), nil}, /* 3 */ {`$(10..0..2) digest ${_}`, int64(2), nil},
} }
// runTestSuiteSpec(t, section, inputs, 2) // runTestSuiteSpec(t, section, inputs, 2)
@@ -166,8 +169,8 @@ func TestMapIterator(t *testing.T) {
section := "Iterator-Map" section := "Iterator-Map"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`$$([3,4,5] map ${_#})`, list.NewLinkedListA(1, 2, 3), nil}, /* 1 */ {`$$([3,4,5] map ${_#})`, list.NewLinkedListA(1, 2, 3), nil},
/* 2 */ {`#$$($(10) map ${_})`, int64(10), nil}, /* 2 */ {`#$$($(..10) map ${_})`, int64(10), nil},
/* 3 */ {`#$$($(10,0) map ${_})`, int64(10), nil}, /* 3 */ {`#$$($(10..0) map ${_})`, int64(10), nil},
/* 4 */ {`builtin "os.file"; $$(fileLineIterator("test-file.txt") map ${__})`, list.NewLinkedListA(0, 1), nil}, /* 4 */ {`builtin "os.file"; $$(fileLineIterator("test-file.txt") map ${__})`, list.NewLinkedListA(0, 1), nil},
/* 5 */ {`$$(["1", "2", "3"] map int())`, nil, `int(): too few params -- expected 1, got 0`}, /* 5 */ {`$$(["1", "2", "3"] map int())`, nil, `int(): too few params -- expected 1, got 0`},
} }