forked from rosa/hakurei
internal/rosa/azalea: implement nil identifier
This also implements a nil-capable string type. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user