// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // operator-dot.go package expr import "fmt" // -------- dot term func newDotTerm(tk *Token) (inst *term) { return &term{ tk: *tk, children: make([]*term, 0, 2), position: posInfix, priority: priDot, evalFunc: evalDot, } } func verifyIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err error) { var v int var indexValue any if indexValue, err = indexTerm.compute(ctx); err == nil { if v, err = indexTerm.toInt(indexValue, "index expression value must be integer"); 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 evalDot(ctx ExprContext, self *term) (v any, err error) { var leftValue, rightValue any if err = self.checkOperands(); err != nil { return } if leftValue, err = self.children[0].compute(ctx); err != nil { return } indexTerm := self.children[1] switch unboxedValue := leftValue.(type) { case *ListType: var index int array := ([]any)(*unboxedValue) if index, err = verifyIndex(ctx, indexTerm, len(array)); err == nil { v = array[index] } case string: var index int if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil { v = string(unboxedValue[index]) } case *DictType: var ok bool var indexValue any if indexValue, err = indexTerm.compute(ctx); err == nil { if v, ok = (*unboxedValue)[indexValue]; !ok { err = fmt.Errorf("key %v does not belong to the dictionary", rightValue) } } case ExtIterator: if indexTerm.symbol() == SymVariable { opName := indexTerm.source() if unboxedValue.HasOperation(opName) { v, err = unboxedValue.CallOperation(opName, []any{}) } else { err = indexTerm.Errorf("this iterator do not support the %q command", opName) v = false } } default: err = self.errIncompatibleTypes(leftValue, rightValue) } return } // init func init() { registerTermConstructor(SymDot, newDotTerm) }