Files
a60d994e25 Improve Performance of the Type System (#824)
* export the checker and ensure no methods or fields are exported

* make Checker reusable

* add PatchAndCheck method to Checker, and reuse it in visitors

* avoid unnecessary map and consolidate structs

* make all Nature methods use pointer-receiver

* added cache for type and nature information

* optimize type and nature checks

* more aggressively cache some partial results and add cheap checks

* cache reused values

* reduce copying in Nature by breaking down optional fields

* enforce use of cache

* improve ergonomics of FromType and NatureOf

* improve struct method fetching

* make cache private to Nature

* first iteration improving struct reflect caching

* cleaned up struct fields data

* improve caching of functions data

* simplify checker code

* simplify and remove dead code

* add checker benchmarks

* address PR comments and minor improvements

* rename from SetCache to Bind

* make the benchmarks more fair

* fix benchmark option not being applied

* move Cache to be argument instead of field

* merge FuncData into Optional

* rename Optional to TypeData

* simplify struct logic

* cache builtin numeric types

---------

Co-authored-by: Anton Medvedev <[email protected]>
2025-09-18 14:38:27 +02:00

77 lines
1.4 KiB
Go

package conf
import (
"fmt"
"reflect"
. "github.com/expr-lang/expr/checker/nature"
"github.com/expr-lang/expr/internal/deref"
"github.com/expr-lang/expr/types"
)
// Env returns the Nature of the given environment.
//
// Deprecated: use EnvWithCache instead.
func Env(env any) Nature {
return EnvWithCache(new(Cache), env)
}
func EnvWithCache(c *Cache, env any) Nature {
if env == nil {
n := c.NatureOf(map[string]any{})
n.Strict = true
return n
}
switch env := env.(type) {
case types.Map:
nt := env.Nature()
return nt
}
v := reflect.ValueOf(env)
t := v.Type()
switch deref.Value(v).Kind() {
case reflect.Struct:
n := c.FromType(t)
n.Strict = true
return n
case reflect.Map:
n := c.FromType(v.Type())
if n.TypeData == nil {
n.TypeData = new(TypeData)
}
n.Strict = true
n.Fields = make(map[string]Nature, v.Len())
for _, key := range v.MapKeys() {
elem := v.MapIndex(key)
if !elem.IsValid() || !elem.CanInterface() {
panic(fmt.Sprintf("invalid map value: %s", key))
}
face := elem.Interface()
switch face := face.(type) {
case types.Map:
nt := face.Nature()
n.Fields[key.String()] = nt
default:
if face == nil {
n.Fields[key.String()] = c.NatureOf(nil)
continue
}
n.Fields[key.String()] = c.NatureOf(face)
}
}
return n
}
panic(fmt.Sprintf("unknown type %T", env))
}