several changes:
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.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user