8e596d5979
1. moved scan/symbol.go to new package sym and adapted all files that reference Symbol type and its values. 2. new type interval defined by begin, end and step values. 3. replaced operator-range.go with operator-interval.go. 4. replaced range operator begin:end with begin..end..step. 5. new implementation of sub-collection extraction based on new interval literal.
104 lines
2.7 KiB
Go
104 lines
2.7 KiB
Go
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
// All rights reserved.
|
|
|
|
// operator-interval.go
|
|
package expr
|
|
|
|
import (
|
|
"git.portale-stac.it/go-pkg/expr/kern"
|
|
"git.portale-stac.it/go-pkg/expr/scan"
|
|
"git.portale-stac.it/go-pkg/expr/sym"
|
|
"git.portale-stac.it/go-pkg/expr/types"
|
|
"git.portale-stac.it/go-pkg/expr/types/interval"
|
|
)
|
|
|
|
// -------- interval term
|
|
|
|
func newIntervalTerm(tk *scan.Token) (inst *scan.Term) {
|
|
return &scan.Term{
|
|
Tk: *tk,
|
|
Children: make([]*scan.Term, 0, 2),
|
|
Position: scan.PosInfix,
|
|
Priority: scan.PriInterval,
|
|
EvalFunc: evalInterval,
|
|
}
|
|
}
|
|
|
|
func changeColonToInterval(t *scan.Term) {
|
|
if t.IsSymbol(sym.SymDoubleDot) {
|
|
t.SetSymbol(sym.SymInterval)
|
|
t.EvalFunc = evalInterval
|
|
if child := t.GetLeftChild(); child != nil && child.IsSymbol(sym.SymDoubleDot) {
|
|
child.SetSymbol(sym.SymInterval)
|
|
}
|
|
}
|
|
}
|
|
|
|
func evalInterval(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|
var begin, end int64
|
|
|
|
step := int64(1)
|
|
switch opTerm.GetChildCount() {
|
|
case 0:
|
|
begin = 0
|
|
end = int64(kern.ConstLastIndex)
|
|
case 1:
|
|
var leftValue any
|
|
if leftValue, err = opTerm.GetChild(0).Compute(ctx); err != nil {
|
|
return
|
|
}
|
|
if begin, err = checkIntervalComponent(opTerm, leftValue); err != nil {
|
|
return
|
|
}
|
|
end = int64(kern.ConstLastIndex)
|
|
default:
|
|
var beginValue, endValue, stepValue any
|
|
subTerm := opTerm.GetLeftChild()
|
|
if subTerm.IsOneOf(sym.SymInterval, sym.SymDoubleDot) {
|
|
if beginValue, endValue, err = subTerm.EvalInfix(ctx); err != nil {
|
|
return
|
|
}
|
|
if stepValue, err = opTerm.GetRightChild().Compute(ctx); err != nil {
|
|
return
|
|
}
|
|
step, err = checkIntervalComponent(opTerm.GetRightChild(), stepValue)
|
|
} else if beginValue, endValue, err = opTerm.EvalInfix(ctx); err != nil {
|
|
return
|
|
}
|
|
if begin, err = checkIntervalComponent(opTerm, beginValue); err != nil {
|
|
return
|
|
}
|
|
if end, err = checkIntervalComponent(opTerm, endValue); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if step <= 0 {
|
|
err = opTerm.Errorf("invalid %s specification: step must be positive", interval.TypeName)
|
|
} else {
|
|
v = interval.NewInterval(begin, end, step)
|
|
}
|
|
return
|
|
}
|
|
|
|
func checkIntervalComponent(t kern.Term, value any) (component int64, err error) {
|
|
if component, err = types.ToGoInt64(value, "%s expression"); err != nil {
|
|
err = t.Errorf(err.Error(), interval.TypeName)
|
|
}
|
|
return
|
|
}
|
|
|
|
func errIntervalInvalidSpecification(t kern.Term) error {
|
|
return t.Errorf("invalid %s specification", interval.TypeName)
|
|
}
|
|
|
|
func errIntervalUnexpectedExpression(t *scan.Term) error {
|
|
return t.Errorf("unexpected %s expression", interval.TypeName)
|
|
}
|
|
|
|
// init
|
|
func init() {
|
|
scan.RegisterTermConstructor(sym.SymInterval, newIntervalTerm)
|
|
scan.RegisterTermConstructor(sym.SymDoubleDot, newIntervalTerm)
|
|
}
|