// 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, self *term) (v any, err error) { var leftValue, rightValue any var indexList *ListType var ok bool if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { return } indexTerm := self.children[1] if indexList, ok = rightValue.(*ListType); !ok { err = self.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: /* var ok bool var indexValue any if indexValue, err = verifyKey(indexTerm, indexList); err == nil { if v, ok = (*unboxedValue)[indexValue]; !ok { err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue) } } */ v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue) default: err = self.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 = self.errIncompatibleTypes(leftValue, rightValue) } } else if IsDict(leftValue) { d := leftValue.(*DictType) /* var ok bool var indexValue any if indexValue, err = verifyKey(indexTerm, indexList); err == nil { if v, ok = (*d)[indexValue]; !ok { err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue) } }*/ v, err = getDictItem(d, indexTerm, indexList, rightValue) } 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) }