new operator "?!" (alternate value)

This commit is contained in:
Celestino Amoroso 2024-07-06 05:54:53 +02:00
parent 1e62a51c15
commit fd8e32e12b
5 changed files with 144 additions and 96 deletions

View File

@ -53,3 +53,9 @@ func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error { func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue) return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
} }
// --- Operator errors
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.source())
}

View File

@ -1,85 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-coalesce.go
package expr
//-------- null coalesce term
func newNullCoalesceTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalNullCoalesce,
}
}
func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
v = rightValue
}
return
}
//-------- coalesce assign term
func newCoalesceAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalAssignCoalesce,
}
}
func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
})
} else {
v = rightValue
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
}
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm)
registerTermConstructor(SymQuestionEqual, newCoalesceAssignTerm)
}

124
operator-default.go Normal file
View File

@ -0,0 +1,124 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-default.go
package expr
//-------- default term
func newDefaultTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalDefault,
}
}
func evalDefault(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, self)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
v = rightValue
}
return
}
//-------- alternate term
func newAlternateTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalAlternate,
}
}
func evalAlternate(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, self)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists && leftValue != nil {
if rightValue, err = self.children[1].compute(ctx); err == nil {
v = rightValue
}
} else {
v = leftValue
}
return
}
//-------- default assign term
func newDefaultAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalAssignDefault,
}
}
func evalAssignDefault(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, self)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
})
} else {
v = rightValue
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
}
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
}

View File

@ -202,8 +202,10 @@ func (self *scanner) fetchNextToken() (tk *Token) {
case '?': case '?':
if next, _ := self.peek(); next == '?' { if next, _ := self.peek(); next == '?' {
tk = self.moveOn(SymDoubleQuestion, ch, next) tk = self.moveOn(SymDoubleQuestion, ch, next)
} else if next, _ := self.peek(); next == '=' { } else if next == '=' {
tk = self.moveOn(SymQuestionEqual, ch, next) tk = self.moveOn(SymQuestionEqual, ch, next)
} else if next == '!' {
tk = self.moveOn(SymQuestionExclam, ch, next)
} else { } else {
tk = self.makeToken(SymQuestion, ch) tk = self.makeToken(SymQuestion, ch)
} }

View File

@ -57,16 +57,17 @@ const (
SymTilde // 46: '~' SymTilde // 46: '~'
SymDoubleQuestion // 47: '??' SymDoubleQuestion // 47: '??'
SymQuestionEqual // 48: '?=' SymQuestionEqual // 48: '?='
SymDoubleAt // 49: '@@' SymQuestionExclam // 49: '?!'
SymDoubleColon // 50: '::' SymDoubleAt // 50: '@@'
SymInsert // 51: '>>' SymDoubleColon // 51: '::'
SymAppend // 52: '<<' SymInsert // 52: '>>'
SymCaret // 53: '^' SymAppend // 53: '<<'
SymDollarRound // 54: '$(' SymCaret // 54: '^'
SymOpenClosedRound // 55: '()' SymDollarRound // 55: '$('
SymDoubleDollar // 56: '$$' SymOpenClosedRound // 56: '()'
SymDoubleDot // 57: '..' SymDoubleDollar // 57: '$$'
SymTripleDot // 58: '...' SymDoubleDot // 58: '..'
SymTripleDot // 59: '...'
SymChangeSign SymChangeSign
SymUnchangeSign SymUnchangeSign
SymIdentifier SymIdentifier