mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
259 lines
4.6 KiB
Go
259 lines
4.6 KiB
Go
package checker
|
|
|
|
import (
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/antonmedv/expr/ast"
|
|
"github.com/antonmedv/expr/conf"
|
|
)
|
|
|
|
var (
|
|
nilType = reflect.TypeOf(nil)
|
|
boolType = reflect.TypeOf(true)
|
|
integerType = reflect.TypeOf(0)
|
|
floatType = reflect.TypeOf(float64(0))
|
|
stringType = reflect.TypeOf("")
|
|
arrayType = reflect.TypeOf([]interface{}{})
|
|
mapType = reflect.TypeOf(map[string]interface{}{})
|
|
anyType = reflect.TypeOf(new(interface{})).Elem()
|
|
timeType = reflect.TypeOf(time.Time{})
|
|
durationType = reflect.TypeOf(time.Duration(0))
|
|
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
|
)
|
|
|
|
func combined(a, b reflect.Type) reflect.Type {
|
|
if a.Kind() == b.Kind() {
|
|
return a
|
|
}
|
|
if isFloat(a) || isFloat(b) {
|
|
return floatType
|
|
}
|
|
return integerType
|
|
}
|
|
|
|
func anyOf(t reflect.Type, fns ...func(reflect.Type) bool) bool {
|
|
for _, fn := range fns {
|
|
if fn(t) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func or(l, r reflect.Type, fns ...func(reflect.Type) bool) bool {
|
|
if isAny(l) && isAny(r) {
|
|
return true
|
|
}
|
|
if isAny(l) && anyOf(r, fns...) {
|
|
return true
|
|
}
|
|
if isAny(r) && anyOf(l, fns...) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isAny(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Interface:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isInteger(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
fallthrough
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isFloat(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isNumber(t reflect.Type) bool {
|
|
return isInteger(t) || isFloat(t)
|
|
}
|
|
|
|
func isTime(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t {
|
|
case timeType:
|
|
return true
|
|
}
|
|
}
|
|
return isAny(t)
|
|
}
|
|
|
|
func isDuration(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t {
|
|
case durationType:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isBool(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Bool:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isString(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.String:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isArray(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Ptr:
|
|
return isArray(t.Elem())
|
|
case reflect.Slice, reflect.Array:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isMap(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Ptr:
|
|
return isMap(t.Elem())
|
|
case reflect.Map:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isStruct(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Ptr:
|
|
return isStruct(t.Elem())
|
|
case reflect.Struct:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isFunc(t reflect.Type) bool {
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Ptr:
|
|
return isFunc(t.Elem())
|
|
case reflect.Func:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func fetchField(t reflect.Type, name string) (reflect.StructField, bool) {
|
|
if t != nil {
|
|
// First check all structs fields.
|
|
for i := 0; i < t.NumField(); i++ {
|
|
field := t.Field(i)
|
|
// Search all fields, even embedded structs.
|
|
if conf.FieldName(field) == name {
|
|
return field, true
|
|
}
|
|
}
|
|
|
|
// Second check fields of embedded structs.
|
|
for i := 0; i < t.NumField(); i++ {
|
|
anon := t.Field(i)
|
|
if anon.Anonymous {
|
|
if field, ok := fetchField(anon.Type, name); ok {
|
|
field.Index = append(anon.Index, field.Index...)
|
|
return field, true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return reflect.StructField{}, false
|
|
}
|
|
|
|
func deref(t reflect.Type) (reflect.Type, bool) {
|
|
if t.Kind() == reflect.Interface {
|
|
return t, true
|
|
}
|
|
found := false
|
|
for t != nil && t.Kind() == reflect.Ptr {
|
|
e := t.Elem()
|
|
switch e.Kind() {
|
|
case reflect.Struct, reflect.Map, reflect.Array, reflect.Slice:
|
|
return t, false
|
|
default:
|
|
found = true
|
|
t = e
|
|
}
|
|
}
|
|
return t, found
|
|
}
|
|
|
|
func isIntegerOrArithmeticOperation(node ast.Node) bool {
|
|
switch n := node.(type) {
|
|
case *ast.IntegerNode:
|
|
return true
|
|
case *ast.UnaryNode:
|
|
switch n.Operator {
|
|
case "+", "-":
|
|
return true
|
|
}
|
|
case *ast.BinaryNode:
|
|
switch n.Operator {
|
|
case "+", "/", "-", "*":
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func setTypeForIntegers(node ast.Node, t reflect.Type) {
|
|
switch n := node.(type) {
|
|
case *ast.IntegerNode:
|
|
n.SetType(t)
|
|
case *ast.UnaryNode:
|
|
switch n.Operator {
|
|
case "+", "-":
|
|
setTypeForIntegers(n.Node, t)
|
|
}
|
|
case *ast.BinaryNode:
|
|
switch n.Operator {
|
|
case "+", "/", "-", "*":
|
|
setTypeForIntegers(n.Left, t)
|
|
setTypeForIntegers(n.Right, t)
|
|
}
|
|
}
|
|
}
|