// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserightChilded. // operator-assign.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/util" ) //-------- assign term func newAssignTerm(tk *scan.Token) (inst *scan.Term) { return &scan.Term{ Tk: *tk, Children: make([]*scan.Term, 0, 2), Position: scan.PosInfix, Priority: scan.PriAssign, EvalFunc: evalAssign, } } func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) { var collectionValue, keyListValue, keyValue any var keyList *kern.ListType var ok bool if collectionValue, err = collectionTerm.Compute(ctx); err != nil { return } if keyListValue, err = keyListTerm.Compute(ctx); err != nil { return } else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 { err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue)) return } if keyValue = (*keyList)[0]; keyValue == nil { err = keyListTerm.Errorf("index/key is nil") return } switch collection := collectionValue.(type) { case *kern.ListType: if index, ok := keyValue.(int64); ok { err = collection.SetItem(index, value) } else { err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue)) } case *kern.DictType: collection.SetItem(keyValue, value) default: err = collectionTerm.Errorf("collection expected") } return } func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any) (err error) { if leftTerm.Symbol() == scan.SymIndex { err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v) } else { ctx.UnsafeSetVar(leftTerm.Source(), v) } return } func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { if err = opTerm.CheckOperands(); err != nil { return } leftTerm := opTerm.Children[0] leftSym := leftTerm.Symbol() if leftSym != scan.SymVariable && leftSym != scan.SymIndex { err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source()) return } rightChild := opTerm.Children[1] if v, err = rightChild.Compute(ctx); err == nil { if functor, ok := v.(kern.Functor); ok { if leftSym == scan.SymVariable { if info := functor.GetFunc(); info != nil { ctx.RegisterFunc(leftTerm.Source(), info.Functor(), info.ReturnType(), info.Params()) } else if funcDef, ok := functor.(*exprFunctor); ok { paramSpecs := util.ForAll(funcDef.params, func(p kern.ExprFuncParam) kern.ExprFuncParam { return p }) ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, paramSpecs) } else { err = opTerm.Errorf("unknown function %s()", rightChild.Source()) } } else { err = assignValue(ctx, leftTerm, v) } } else { err = assignValue(ctx, leftTerm, v) } } if err != nil { v = nil } return } //-------- assign term func newOpAssignTerm(tk *scan.Token) (inst *scan.Term) { return &scan.Term{ Tk: *tk, Children: make([]*scan.Term, 0, 2), Position: scan.PosInfix, Priority: scan.PriAssign, EvalFunc: evalOpAssign, } } func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term) (value any, err error) { var collectionValue, keyListValue, keyValue any var keyList *kern.ListType var ok bool if collectionValue, err = collectionTerm.Compute(ctx); err != nil { return } if keyListValue, err = keyListTerm.Compute(ctx); err != nil { return } else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 { err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue)) return } if keyValue = (*keyList)[0]; keyValue == nil { err = keyListTerm.Errorf("index/key is nil") return } switch collection := collectionValue.(type) { case *kern.ListType: if index, ok := keyValue.(int64); ok { value = (*collection)[index] } else { err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue)) } case *kern.DictType: value = (*collection)[keyValue] default: err = collectionTerm.Errorf("collection expected") } return } func getAssignValue(ctx kern.ExprContext, leftTerm *scan.Term) (value any, err error) { if leftTerm.Symbol() == scan.SymIndex { value, err = getCollectionItemValue(ctx, leftTerm.Children[0], leftTerm.Children[1]) } else { value, _ = ctx.GetVar(leftTerm.Source()) } return } func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { var rightValue, leftValue any if err = opTerm.CheckOperands(); err != nil { return } leftTerm := opTerm.Children[0] leftSym := leftTerm.Symbol() if leftSym != scan.SymVariable && leftSym != scan.SymIndex { err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source()) return } rightChild := opTerm.Children[1] if rightValue, err = rightChild.Compute(ctx); err == nil { if leftValue, err = getAssignValue(ctx, leftTerm); err == nil { switch opTerm.Symbol() { case scan.SymPlusEqual: v, err = sumValues(opTerm, leftValue, rightValue) case scan.SymMinusEqual: v, err = diffValues(opTerm, leftValue, rightValue) case scan.SymStarEqual: v, err = mulValues(opTerm, leftValue, rightValue) case scan.SymSlashEqual: v, err = divValues(opTerm, leftValue, rightValue) case scan.SymPercEqual: v, err = remainderValues(opTerm, leftValue, rightValue) case scan.SymAmpersandEqual: v, err = bitwiseAnd(opTerm, leftValue, rightValue) case scan.SymVertBarEqual: v, err = bitwiseOr(opTerm, leftValue, rightValue) case scan.SymCaretEqual: v, err = bitwiseXor(opTerm, leftValue, rightValue) case scan.SymDoubleLessEqual: v, err = bitLeftShift(opTerm, leftValue, rightValue) case scan.SymDoubleGreaterEqual: v, err = bitRightShift(opTerm, leftValue, rightValue) default: err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source()) } if err == nil { err = assignValue(ctx, leftTerm, v) } } } return } // init func init() { scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm) scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymSlashEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymPercEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymDoubleLessEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymDoubleGreaterEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymAmpersandEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymVertBarEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymCaretEqual, newOpAssignTerm) }