221 lines
6.4 KiB
Go
221 lines
6.4 KiB
Go
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
// All rights reserightChilded.
|
|
|
|
// operator-assign.go
|
|
package expr
|
|
|
|
//-------- assign term
|
|
|
|
func newAssignTerm(tk *Token) (inst *term) {
|
|
return &term{
|
|
tk: *tk,
|
|
children: make([]*term, 0, 2),
|
|
position: posInfix,
|
|
priority: priAssign,
|
|
evalFunc: evalAssign,
|
|
}
|
|
}
|
|
|
|
func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, value any) (err error) {
|
|
var collectionValue, keyListValue, keyValue any
|
|
var keyList *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.(*ListType); !ok || len(*keyList) != 1 {
|
|
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
|
return
|
|
}
|
|
if keyValue = (*keyList)[0]; keyValue == nil {
|
|
err = keyListTerm.Errorf("index/key is nil")
|
|
return
|
|
}
|
|
|
|
switch collection := collectionValue.(type) {
|
|
case *ListType:
|
|
if index, ok := keyValue.(int64); ok {
|
|
err = collection.setItem(index, value)
|
|
} else {
|
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
|
|
}
|
|
case *DictType:
|
|
err = collection.setItem(keyValue, value)
|
|
default:
|
|
err = collectionTerm.Errorf("collection expected")
|
|
}
|
|
return
|
|
}
|
|
|
|
func assignValue(ctx ExprContext, leftTerm *term, v any) (err error) {
|
|
if leftTerm.symbol() == SymIndex {
|
|
err = assignCollectionItem(ctx, leftTerm.children[0], leftTerm.children[1], v)
|
|
} else {
|
|
ctx.UnsafeSetVar(leftTerm.source(), v)
|
|
}
|
|
return
|
|
}
|
|
|
|
func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
|
if err = opTerm.checkOperands(); err != nil {
|
|
return
|
|
}
|
|
|
|
leftTerm := opTerm.children[0]
|
|
leftSym := leftTerm.symbol()
|
|
if leftSym != SymVariable && leftSym != 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.(Functor); ok {
|
|
if leftSym == 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 := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p })
|
|
|
|
ctx.RegisterFunc(leftTerm.source(), functor, 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 *Token) (inst *term) {
|
|
return &term{
|
|
tk: *tk,
|
|
children: make([]*term, 0, 2),
|
|
position: posInfix,
|
|
priority: priAssign,
|
|
evalFunc: evalOpAssign,
|
|
}
|
|
}
|
|
|
|
func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
|
|
var collectionValue, keyListValue, keyValue any
|
|
var keyList *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.(*ListType); !ok || len(*keyList) != 1 {
|
|
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
|
return
|
|
}
|
|
if keyValue = (*keyList)[0]; keyValue == nil {
|
|
err = keyListTerm.Errorf("index/key is nil")
|
|
return
|
|
}
|
|
|
|
switch collection := collectionValue.(type) {
|
|
case *ListType:
|
|
if index, ok := keyValue.(int64); ok {
|
|
value = (*collection)[index]
|
|
} else {
|
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
|
|
}
|
|
case *DictType:
|
|
value = (*collection)[keyValue]
|
|
default:
|
|
err = collectionTerm.Errorf("collection expected")
|
|
}
|
|
return
|
|
}
|
|
|
|
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) {
|
|
if leftTerm.symbol() == SymIndex {
|
|
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
|
|
} else {
|
|
value, _ = ctx.GetVar(leftTerm.source())
|
|
}
|
|
return
|
|
}
|
|
|
|
func evalOpAssign(ctx ExprContext, opTerm *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 != SymVariable && leftSym != 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 SymPlusEqual:
|
|
v, err = sumValues(opTerm, leftValue, rightValue)
|
|
case SymMinusEqual:
|
|
v, err = diffValues(opTerm, leftValue, rightValue)
|
|
case SymStarEqual:
|
|
v, err = mulValues(opTerm, leftValue, rightValue)
|
|
case SymSlashEqual:
|
|
v, err = divValues(opTerm, leftValue, rightValue)
|
|
case SymPercEqual:
|
|
v, err = remainderValues(opTerm, leftValue, rightValue)
|
|
case SymAmpersandEqual:
|
|
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
|
case SymVertBarEqual:
|
|
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
|
case SymCaretEqual:
|
|
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
|
case SymDoubleLessEqual:
|
|
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
|
case 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() {
|
|
registerTermConstructor(SymEqual, newAssignTerm)
|
|
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymPercEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymVertBarEqual, newOpAssignTerm)
|
|
registerTermConstructor(SymCaretEqual, newOpAssignTerm)
|
|
}
|