diff options
Diffstat (limited to 'internal/rosa/azalea/evaluate.go')
| -rw-r--r-- | internal/rosa/azalea/evaluate.go | 65 |
1 files changed, 59 insertions, 6 deletions
diff --git a/internal/rosa/azalea/evaluate.go b/internal/rosa/azalea/evaluate.go index 044e6826..b35a056a 100644 --- a/internal/rosa/azalea/evaluate.go +++ b/internal/rosa/azalea/evaluate.go @@ -12,7 +12,7 @@ import ( // Value are types supported by the language. type Value interface { - bool | int64 | string | []string | []int64 | [][2]string + bool | int64 | string | []string | []int64 | [][2]string | Nil } type ( @@ -33,6 +33,29 @@ type ( F func(args FArgs) (v any, set bool, err error) V map[unique.Handle[Ident]]any } + + // Nil represents an untyped nil. + Nil struct{} + + // AString represents an argument string that may be nil. + AString struct { + V string + Nil bool + } +) + +// NilError is returned for an invalid nil assignment. +type NilError struct{ reflect.Type } + +func (e NilError) Error() string { + return fmt.Sprintf("attempting to assign nil to %s", e.Type) +} + +var ( + // nilStringV is the [reflect.Value] of an Azalea nil assigned to an [AString]. + nilStringV = reflect.ValueOf(AString{Nil: true}) + // nilStringT is the [reflect.Type] of nilStringV. + nilStringT = nilStringV.Type() ) // Apply applies named arguments and rejects unused arguments. @@ -50,8 +73,8 @@ func (args FArgs) Apply(v map[unique.Handle[Ident]]any) error { } return UndefinedError(arg.K.Value()) } - err := storeE(r, arg.V) - if err != nil { + + if err := storeE(r, arg.V); err != nil { return err } } @@ -115,11 +138,38 @@ func (e TypeError) Is(err error) bool { } // storeE is a convenience function to set the value of a result pointer. -func storeE(rp any, r any) error { +func storeE(rp, r any) error { pv := reflect.ValueOf(rp).Elem() + pt := pv.Type() + as := nilStringT.AssignableTo(pt) + + if r == (Nil{}) { + switch kind := pt.Kind(); kind { + case reflect.Bool, reflect.String: + return NilError{pt} + default: + if rv, ok := rp.(*any); ok && !reflect.ValueOf(*rv).IsValid() { + *rv = Nil{} + } else if as { + pv.Set(nilStringV) + } else { + pv.SetZero() + } + return nil + } + } + v := reflect.ValueOf(r) - pt, vt := pv.Type(), v.Type() - if !vt.AssignableTo(pt) { + if vt := v.Type(); !vt.AssignableTo(pt) { + if as { + var asv AString + if err := storeE(&asv.V, r); err != nil { + return err + } + pv.Set(reflect.ValueOf(asv)) + return nil + } + return TypeError{vt, pt} } pv.Set(v) @@ -226,6 +276,9 @@ func evaluateAny(d PF, s []Frame, expr, rp any) bool { case "false": store(rp, false) return true + case "nil": + store(rp, Nil{}) + return true default: return evaluateAny(d, s, v, rp) } |
