Files
expr/operator-assign.go
T

287 lines
8.5 KiB
Go

// 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 evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
v, err = generalEvalAssign(ctx, opTerm, false)
return
}
func newDeepCopyAssignTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriAssign,
EvalFunc: evalDeepCopyAssign,
}
}
func evalDeepCopyAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
v, err = generalEvalAssign(ctx, opTerm, true)
return
}
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, deepCopy bool) (err error) {
if leftTerm.Symbol() == scan.SymIndex {
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
} else {
if deepCopy {
v = kern.Clone(v)
}
ctx.UnsafeSetVar(leftTerm.Source(), v)
}
return
}
func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern.Term) (v any, err error) {
var ok bool
var dictAny, dotKey any
var dict *kern.DictType
if err = dotTerm.CheckOperands(); err != nil {
return
}
dotLeftTerm := dotTerm.GetChild(0)
if dictAny, err = dotLeftTerm.Compute(ctx); err != nil {
return
}
if dict, ok = dictAny.(*kern.DictType); !ok {
err = dotTerm.Tk.ErrorExpectedGot(kern.DictTypeName)
return
}
dotRightTerm := dotTerm.Children[1]
dotRightSym := dotRightTerm.Symbol()
if dotRightSym == scan.SymVariable || dotRightSym == scan.SymString {
dotKey = util.UnquoteString(dotRightTerm.Source())
} else if dotKey, err = dotRightTerm.Compute(ctx); err != nil {
return
}
if v, err = valueTerm.Compute(ctx); err != nil {
return
}
dict.SetItem(dotKey, v)
return
}
func generalEvalAssign(ctx kern.ExprContext, opTerm *scan.Term, deepCopy bool) (v any, err error) {
if err = opTerm.CheckOperands(); err != nil {
return
}
leftTerm := opTerm.Children[0]
leftSym := leftTerm.Symbol()
if leftSym == scan.SymDot {
return evalAssignDictItem(ctx, opTerm.Children[0], opTerm.GetChild(1))
} else 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, deepCopy)
}
} else {
err = assignValue(ctx, leftTerm, v, deepCopy)
}
}
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, false)
}
}
}
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm)
scan.RegisterTermConstructor(scan.SymColonEqual, newDeepCopyAssignTerm)
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)
}