From 3319ba8cdd2f5872e673d884cd9a5953565a5ea6 Mon Sep 17 00:00:00 2001 From: Anton Medvedev Date: Sun, 2 Jun 2024 23:48:25 +0200 Subject: [PATCH] Move SubType to Nature --- checker/checker.go | 25 +++--------- checker/nature/nature.go | 47 ++++++++-------------- checker/nature/of.go | 47 ++++++++++++++++++++++ checker/nature/types.go | 84 ---------------------------------------- checker/types.go | 7 ++++ docgen/docgen.go | 2 +- 6 files changed, 77 insertions(+), 135 deletions(-) create mode 100644 checker/nature/of.go delete mode 100644 checker/nature/types.go diff --git a/checker/checker.go b/checker/checker.go index a6e41dd8..c48eff24 100644 --- a/checker/checker.go +++ b/checker/checker.go @@ -410,16 +410,10 @@ func (v *checker) BinaryNode(node *ast.BinaryNode) Nature { case "..": if isInteger(l) && isInteger(r) { - return Nature{ - Type: arrayType, - SubType: Array{Elem: integerNature}, - } + return arrayOf(integerNature) } if or(l, r, isInteger) { - return Nature{ - Type: arrayType, - SubType: Array{Elem: integerNature}, - } + return arrayOf(integerNature) } case "??": @@ -650,10 +644,7 @@ func (v *checker) BuiltinNode(node *ast.BuiltinNode) Nature { if isUnknown(collection) { return arrayNature } - return Nature{ - Type: arrayType, - SubType: Array{Elem: collection.Elem()}, - } + return arrayOf(collection.Elem()) } return v.error(node.Arguments[1], "predicate should has one input and one output param") @@ -671,10 +662,7 @@ func (v *checker) BuiltinNode(node *ast.BuiltinNode) Nature { closure.NumOut() == 1 && closure.NumIn() == 1 && isUnknown(closure.In(0)) { - return Nature{ - Type: arrayType, - SubType: Array{Elem: closure.Out(0)}, - } + return arrayOf(closure.Out(0)) } return v.error(node.Arguments[1], "predicate should has one input and one output param") @@ -1199,10 +1187,7 @@ func (v *checker) ArrayNode(node *ast.ArrayNode) Nature { prev = curr } if allElementsAreSameType { - return Nature{ - Type: arrayNature.Type, - SubType: Array{Elem: prev}, - } + return arrayOf(prev) } return arrayNature } diff --git a/checker/nature/nature.go b/checker/nature/nature.go index a46cee3c..14619ca1 100644 --- a/checker/nature/nature.go +++ b/checker/nature/nature.go @@ -12,19 +12,18 @@ var ( ) type Nature struct { - Type reflect.Type - Nil bool - SubType SubType - Func *builtin.Function - Method bool - MethodIndex int - FieldIndex []int + 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). + Fields map[string]Nature // Fields 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) String() string { - if n.SubType != nil { - return n.SubType.String() - } if n.Type != nil { return n.Type.String() } @@ -57,8 +56,8 @@ func (n Nature) Elem() Nature { case reflect.Map, reflect.Ptr: return Nature{Type: n.Type.Elem()} case reflect.Array, reflect.Slice: - if array, ok := n.SubType.(Array); ok { - return array.Elem + if n.ArrayOf != nil { + return *n.ArrayOf } return Nature{Type: n.Type.Elem()} } @@ -180,14 +179,14 @@ func (n Nature) Get(name string) (Nature, bool) { }, true } case reflect.Map: - if f, ok := n.SubType.Get(name); ok { + if f, ok := n.Fields[name]; ok { return f, true } } return unknown, false } -func (n Nature) List() map[string]Nature { +func (n Nature) All() map[string]Nature { table := make(map[string]Nature) if n.Type == nil { @@ -215,23 +214,11 @@ func (n Nature) List() map[string]Nature { } case reflect.Map: - if st, ok := n.SubType.(Map); ok { - for key, nt := range st.Fields { - if _, ok := table[key]; ok { - continue - } - table[key] = nt - } - } - v := reflect.ValueOf(n.SubType) - if v.Kind() != reflect.Map { - break - } - for _, key := range v.MapKeys() { - value := v.MapIndex(key) - if key.Kind() == reflect.String && value.IsValid() && value.CanInterface() { - table[key.String()] = Nature{Type: reflect.TypeOf(value.Interface())} + for key, nt := range n.Fields { + if _, ok := table[key]; ok { + continue } + table[key] = nt } } diff --git a/checker/nature/of.go b/checker/nature/of.go new file mode 100644 index 00000000..93235f9c --- /dev/null +++ b/checker/nature/of.go @@ -0,0 +1,47 @@ +package nature + +import ( + "fmt" + "reflect" + + "github.com/expr-lang/expr/types" +) + +func Of(value any) Nature { + if value == nil { + return Nature{Nil: true} + } + + v := reflect.ValueOf(value) + + switch v.Kind() { + case reflect.Map: + _, strict := value.(types.StrictMap) + 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.(type) { + case types.Map, types.StrictMap: + fields[key.String()] = Of(face) + default: + if face == nil { + fields[key.String()] = Nature{Nil: true} + continue + } + fields[key.String()] = Nature{Type: reflect.TypeOf(face)} + + } + } + return Nature{ + Type: v.Type(), + Fields: fields, + Strict: strict, + } + } + + return Nature{Type: v.Type()} +} diff --git a/checker/nature/types.go b/checker/nature/types.go deleted file mode 100644 index 0cc1aadc..00000000 --- a/checker/nature/types.go +++ /dev/null @@ -1,84 +0,0 @@ -package nature - -import ( - "fmt" - "reflect" - - "github.com/expr-lang/expr/types" -) - -func Of(value any) Nature { - if value == nil { - return Nature{Nil: true} - } - - v := reflect.ValueOf(value) - - switch v.Kind() { - case reflect.Map: - _, strict := value.(types.StrictMap) - subMap := Map{ - Elem: Nature{Type: v.Type().Elem()}, - Fields: make(map[string]Nature, v.Len()), - Strict: strict, - } - 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.(type) { - case types.Map, types.StrictMap: - subMap.Fields[key.String()] = Of(face) - default: - if face == nil { - subMap.Fields[key.String()] = Nature{Nil: true} - continue - } - subMap.Fields[key.String()] = Nature{Type: reflect.TypeOf(face)} - - } - } - return Nature{ - Type: v.Type(), - SubType: subMap, - } - } - - return Nature{Type: v.Type()} -} - -type SubType interface { - String() string - Get(name string) (Nature, bool) -} - -type Array struct { - Elem Nature -} - -func (a Array) String() string { - return "[]" + a.Elem.String() -} - -func (a Array) Get(name string) (Nature, bool) { - return unknown, false -} - -type Map struct { - Elem Nature - Fields map[string]Nature - Strict bool -} - -func (m Map) String() string { - return "map[string]" + m.Elem.String() -} - -func (m Map) Get(name string) (Nature, bool) { - if n, ok := m.Fields[name]; ok { - return n, true - } - return unknown, false -} diff --git a/checker/types.go b/checker/types.go index c5ba248a..ef93cf03 100644 --- a/checker/types.go +++ b/checker/types.go @@ -27,6 +27,13 @@ var ( arrayType = reflect.TypeOf([]any{}) ) +func arrayOf(nt Nature) Nature { + return Nature{ + Type: arrayType, + ArrayOf: &nt, + } +} + func isNil(nt Nature) bool { return nt.Nil } diff --git a/docgen/docgen.go b/docgen/docgen.go index 2eed5ca6..d9abcf0e 100644 --- a/docgen/docgen.go +++ b/docgen/docgen.go @@ -84,7 +84,7 @@ func CreateDoc(i any) *Context { PkgPath: deref.Type(reflect.TypeOf(i)).PkgPath(), } - for name, t := range nature.Of(i).List() { + for name, t := range nature.Of(i).All() { if _, ok := c.Variables[Identifier(name)]; ok { continue }