Skip to content

Commit

Permalink
Add types.Equal
Browse files Browse the repository at this point in the history
  • Loading branch information
antonmedv committed Jun 14, 2024
1 parent 55f453b commit 0c7f720
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 5 deletions.
99 changes: 94 additions & 5 deletions types/types.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package types

import (
"fmt"
"reflect"
"strings"

. "github.com/expr-lang/expr/checker/nature"
)

func TypeOf(v any) Type {
return rtype{t: reflect.TypeOf(v)}
// Type is a type that can be used to represent a value.
type Type interface {
Nature() Nature
Equal(Type) bool
String() string
}

var (
Expand All @@ -28,9 +33,11 @@ var (
Nil = nilType{}
)

// Type is a type that can be used to represent a value.
type Type interface {
Nature() Nature
func TypeOf(v any) Type {
if v == nil {
return Nil
}
return rtype{t: reflect.TypeOf(v)}
}

type nilType struct{}
Expand All @@ -39,6 +46,14 @@ func (nilType) Nature() Nature {
return Nature{Nil: true}
}

func (nilType) Equal(t Type) bool {
return t == Nil
}

func (nilType) String() string {
return "nil"
}

type rtype struct {
t reflect.Type
}
Expand All @@ -47,6 +62,17 @@ func (r rtype) Nature() Nature {
return Nature{Type: r.t}
}

func (r rtype) Equal(t Type) bool {
if rt, ok := t.(rtype); ok {
return r.t.String() == rt.t.String()
}
return false
}

func (r rtype) String() string {
return r.t.String()
}

// Map returns a type that represents a map of the given type.
// The map is not strict, meaning that it can contain keys not defined in the map.
type Map map[string]Type
Expand All @@ -62,6 +88,30 @@ func (m Map) Nature() Nature {
return nt
}

func (m Map) Equal(t Type) bool {
mt, ok := t.(Map)
if !ok {
return false
}
if len(m) != len(mt) {
return false
}
for k, v := range m {
if !v.Equal(mt[k]) {
return false
}
}
return true
}

func (m Map) String() string {
pairs := make([]string, 0, len(m))
for k, v := range m {
pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String()))
}
return fmt.Sprintf("Map{%s}", strings.Join(pairs, ", "))
}

// StrictMap returns a type that represents a map of the given type.
// The map is strict, meaning that it can only contain keys defined in the map.
type StrictMap map[string]Type
Expand All @@ -78,6 +128,30 @@ func (m StrictMap) Nature() Nature {
return nt
}

func (m StrictMap) Equal(t Type) bool {
mt, ok := t.(StrictMap)
if !ok {
return false
}
if len(m) != len(mt) {
return false
}
for k, v := range m {
if !v.Equal(mt[k]) {
return false
}
}
return true
}

func (m StrictMap) String() string {
pairs := make([]string, 0, len(m))
for k, v := range m {
pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String()))
}
return fmt.Sprintf("StrictMap{%s}", strings.Join(pairs, ", "))
}

// Array returns a type that represents an array of the given type.
func Array(of Type) Type {
return array{of}
Expand All @@ -95,3 +169,18 @@ func (a array) Nature() Nature {
ArrayOf: &of,
}
}

func (a array) Equal(t Type) bool {
at, ok := t.(array)
if !ok {
return false
}
if a.of.Equal(at.of) {
return true
}
return false
}

func (a array) String() string {
return fmt.Sprintf("Array{%s}", a.of.String())
}
47 changes: 47 additions & 0 deletions types/types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package types_test

import (
"testing"

"github.com/expr-lang/expr/internal/testify/require"
. "github.com/expr-lang/expr/types"
)

func TestType_Equal(t *testing.T) {
tests := []struct {
index string
a, b Type
want bool
}{
{"1", Int, Int, true},
{"2", Int, Int8, false},
{"3", Int, Uint, false},
{"4", Int, Float, false},
{"5", Int, String, false},
{"6", Int, Bool, false},
{"7", Int, Nil, false},
{"8", Int, Array(Int), false},
{"9", Int, Map{"foo": Int}, false},
{"10", Int, StrictMap{"foo": Int}, false},
{"11", Int, Array(Int), false},
{"12", Array(Int), Array(Int), true},
{"13", Array(Int), Array(Float), false},
{"14", Map{"foo": Int}, Map{"foo": Int}, true},
{"15", Map{"foo": Int}, Map{"foo": Float}, false},
{"16", Map{"foo": Int}, StrictMap{"foo": Int}, false},
{"17", StrictMap{"foo": Int}, StrictMap{"foo": Int}, true},
{"18", StrictMap{"foo": Int}, StrictMap{"foo": Float}, false},
{"19", Map{"foo": Map{"bar": Int}}, Map{"foo": Map{"bar": Int}}, true},
{"20", Map{"foo": Map{"bar": Int}}, Map{"foo": Map{"bar": Float}}, false},
}

for _, tt := range tests {
t.Run(tt.index, func(t *testing.T) {
if tt.want {
require.True(t, tt.a.Equal(tt.b), tt.a.String()+" == "+tt.b.String())
} else {
require.False(t, tt.a.Equal(tt.b), tt.a.String()+" == "+tt.b.String())
}
})
}
}

0 comments on commit 0c7f720

Please sign in to comment.