Also, fixed and improved some parsing sections concerning collection indeces and ranges
139 lines
3.7 KiB
Go
139 lines
3.7 KiB
Go
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
// All rights reserved.
|
|
|
|
// operator-index.go
|
|
package expr
|
|
|
|
// -------- index term
|
|
func newIndexTerm(tk *Token) (inst *term) {
|
|
return &term{
|
|
tk: *tk,
|
|
children: make([]*term, 0, 2),
|
|
position: posInfix,
|
|
priority: priDot,
|
|
evalFunc: evalIndex,
|
|
}
|
|
}
|
|
|
|
func verifyKey(indexList *ListType) (index any, err error) {
|
|
index = (*indexList)[0]
|
|
return
|
|
}
|
|
|
|
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
|
|
var v int
|
|
|
|
if v, err = ToGoInt((*indexList)[0], "index expression"); err == nil {
|
|
if v < 0 && v >= -maxValue {
|
|
v = maxValue + v
|
|
}
|
|
if v >= 0 && v < maxValue {
|
|
index = v
|
|
} else {
|
|
err = indexTerm.Errorf("index %d out of bounds", v)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex, endIndex int, err error) {
|
|
v, _ := ((*indexList)[0]).(*intPair)
|
|
startIndex = v.a
|
|
endIndex = v.b
|
|
if endIndex == ConstLastIndex {
|
|
endIndex = maxValue
|
|
}
|
|
if startIndex < 0 && startIndex >= -maxValue {
|
|
startIndex = maxValue + startIndex
|
|
}
|
|
if endIndex < 0 && endIndex >= -maxValue {
|
|
endIndex = maxValue + endIndex
|
|
}
|
|
if startIndex < 0 || startIndex > maxValue {
|
|
err = indexTerm.Errorf("range start-index %d is out of bounds", startIndex)
|
|
} else if endIndex < 0 || endIndex > maxValue {
|
|
err = indexTerm.Errorf("range end-index %d is out of bounds", endIndex)
|
|
} else if startIndex > endIndex {
|
|
err = indexTerm.Errorf("range start-index %d must not be greater than end-index %d", startIndex, endIndex)
|
|
}
|
|
return
|
|
}
|
|
|
|
func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
|
var leftValue, rightValue any
|
|
var indexList *ListType
|
|
var ok bool
|
|
|
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
return
|
|
}
|
|
|
|
indexTerm := opTerm.children[1]
|
|
if indexList, ok = rightValue.(*ListType); !ok {
|
|
err = opTerm.Errorf("invalid index expression")
|
|
return
|
|
} else if len(*indexList) != 1 {
|
|
err = indexTerm.Errorf("one index only is allowed")
|
|
return
|
|
}
|
|
|
|
if IsInteger((*indexList)[0]) {
|
|
switch unboxedValue := leftValue.(type) {
|
|
case *ListType:
|
|
var index int
|
|
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
|
v = (*unboxedValue)[index]
|
|
}
|
|
case string:
|
|
var index int
|
|
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
|
|
v = string(unboxedValue[index])
|
|
}
|
|
case *DictType:
|
|
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
|
|
default:
|
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
}
|
|
} else if isIntPair((*indexList)[0]) {
|
|
switch unboxedValue := leftValue.(type) {
|
|
case *ListType:
|
|
var start, end int
|
|
if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
|
sublist := ListType((*unboxedValue)[start:end])
|
|
v = &sublist
|
|
}
|
|
case string:
|
|
var start, end int
|
|
if start, end, err = verifyRange(indexTerm, indexList, len(unboxedValue)); err == nil {
|
|
v = unboxedValue[start:end]
|
|
}
|
|
default:
|
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
}
|
|
} else if IsDict(leftValue) {
|
|
d := leftValue.(*DictType)
|
|
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
|
} else {
|
|
rightChild := opTerm.children[1]
|
|
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
|
|
}
|
|
return
|
|
}
|
|
|
|
func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue any) (v any, err error) {
|
|
var ok bool
|
|
var indexValue any
|
|
|
|
if indexValue, err = verifyKey(indexList); err == nil {
|
|
if v, ok = (*d)[indexValue]; !ok {
|
|
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// init
|
|
func init() {
|
|
registerTermConstructor(SymIndex, newIndexTerm)
|
|
}
|