Files
kubevpn/vendor/github.com/expr-lang/expr/checker/nature/nature.go
2025-04-19 10:06:56 +08:00

262 lines
5.2 KiB
Go

package nature
import (
"reflect"
"github.com/expr-lang/expr/builtin"
"github.com/expr-lang/expr/internal/deref"
)
var (
unknown = Nature{}
)
type Nature struct {
Type reflect.Type // Type of the value. If nil, then value is unknown.
Func *builtin.Function // Used to pass function type from callee to CallNode.
ArrayOf *Nature // Elem nature of array type (usually Type is []any, but ArrayOf can be any nature).
PredicateOut *Nature // Out nature of predicate.
Fields map[string]Nature // Fields of map type.
DefaultMapValue *Nature // Default value of map type.
Strict bool // If map is types.StrictMap.
Nil bool // If value is nil.
Method bool // If value retrieved from method. Usually used to determine amount of in arguments.
MethodIndex int // Index of method in type.
FieldIndex []int // Index of field in type.
}
func (n Nature) IsAny() bool {
return n.Kind() == reflect.Interface && n.NumMethods() == 0
}
func (n Nature) IsUnknown() bool {
switch {
case n.Type == nil && !n.Nil:
return true
case n.IsAny():
return true
}
return false
}
func (n Nature) String() string {
if n.Type != nil {
return n.Type.String()
}
return "unknown"
}
func (n Nature) Deref() Nature {
if n.Type != nil {
n.Type = deref.Type(n.Type)
}
return n
}
func (n Nature) Kind() reflect.Kind {
if n.Type != nil {
return n.Type.Kind()
}
return reflect.Invalid
}
func (n Nature) Key() Nature {
if n.Kind() == reflect.Map {
return Nature{Type: n.Type.Key()}
}
return unknown
}
func (n Nature) Elem() Nature {
switch n.Kind() {
case reflect.Ptr:
return Nature{Type: n.Type.Elem()}
case reflect.Map:
if n.DefaultMapValue != nil {
return *n.DefaultMapValue
}
return Nature{Type: n.Type.Elem()}
case reflect.Array, reflect.Slice:
if n.ArrayOf != nil {
return *n.ArrayOf
}
return Nature{Type: n.Type.Elem()}
}
return unknown
}
func (n Nature) AssignableTo(nt Nature) bool {
if n.Nil {
// Untyped nil is assignable to any interface, but implements only the empty interface.
if nt.IsAny() {
return true
}
}
if n.Type == nil || nt.Type == nil {
return false
}
return n.Type.AssignableTo(nt.Type)
}
func (n Nature) NumMethods() int {
if n.Type == nil {
return 0
}
return n.Type.NumMethod()
}
func (n Nature) MethodByName(name string) (Nature, bool) {
if n.Type == nil {
return unknown, false
}
method, ok := n.Type.MethodByName(name)
if !ok {
return unknown, false
}
if n.Type.Kind() == reflect.Interface {
// In case of interface type method will not have a receiver,
// and to prevent checker decreasing numbers of in arguments
// return method type as not method (second argument is false).
// Also, we can not use m.Index here, because it will be
// different indexes for different types which implement
// the same interface.
return Nature{Type: method.Type}, true
} else {
return Nature{
Type: method.Type,
Method: true,
MethodIndex: method.Index,
}, true
}
}
func (n Nature) NumIn() int {
if n.Type == nil {
return 0
}
return n.Type.NumIn()
}
func (n Nature) In(i int) Nature {
if n.Type == nil {
return unknown
}
return Nature{Type: n.Type.In(i)}
}
func (n Nature) NumOut() int {
if n.Type == nil {
return 0
}
return n.Type.NumOut()
}
func (n Nature) Out(i int) Nature {
if n.Type == nil {
return unknown
}
return Nature{Type: n.Type.Out(i)}
}
func (n Nature) IsVariadic() bool {
if n.Type == nil {
return false
}
return n.Type.IsVariadic()
}
func (n Nature) FieldByName(name string) (Nature, bool) {
if n.Type == nil {
return unknown, false
}
field, ok := fetchField(n.Type, name)
return Nature{Type: field.Type, FieldIndex: field.Index}, ok
}
func (n Nature) PkgPath() string {
if n.Type == nil {
return ""
}
return n.Type.PkgPath()
}
func (n Nature) IsFastMap() bool {
if n.Type == nil {
return false
}
if n.Type.Kind() == reflect.Map &&
n.Type.Key().Kind() == reflect.String &&
n.Type.Elem().Kind() == reflect.Interface {
return true
}
return false
}
func (n Nature) Get(name string) (Nature, bool) {
if n.Type == nil {
return unknown, false
}
if m, ok := n.MethodByName(name); ok {
return m, true
}
t := deref.Type(n.Type)
switch t.Kind() {
case reflect.Struct:
if f, ok := fetchField(t, name); ok {
return Nature{
Type: f.Type,
FieldIndex: f.Index,
}, true
}
case reflect.Map:
if f, ok := n.Fields[name]; ok {
return f, true
}
}
return unknown, false
}
func (n Nature) All() map[string]Nature {
table := make(map[string]Nature)
if n.Type == nil {
return table
}
for i := 0; i < n.Type.NumMethod(); i++ {
method := n.Type.Method(i)
table[method.Name] = Nature{
Type: method.Type,
Method: true,
MethodIndex: method.Index,
}
}
t := deref.Type(n.Type)
switch t.Kind() {
case reflect.Struct:
for name, nt := range StructFields(t) {
if _, ok := table[name]; ok {
continue
}
table[name] = nt
}
case reflect.Map:
for key, nt := range n.Fields {
if _, ok := table[key]; ok {
continue
}
table[key] = nt
}
}
return table
}