Skip to content

Commit caa191d

Browse files
committed
Refactor types table
1 parent 9b6b157 commit caa191d

File tree

10 files changed

+61
-42
lines changed

10 files changed

+61
-42
lines changed

checker/checker.go

+5-9
Original file line numberDiff line numberDiff line change
@@ -1101,17 +1101,13 @@ func traverseAndReplaceIntegerNodesWithIntegerNodes(node *ast.Node, newNature Na
11011101

11021102
func (v *checker) ClosureNode(node *ast.ClosureNode) Nature {
11031103
nt := v.visit(node.Node)
1104-
var out reflect.Type
1104+
var out []reflect.Type
11051105
if isUnknown(nt) {
1106-
out = anyType
1107-
} else {
1108-
out = nt.Type
1106+
out = append(out, anyType)
1107+
} else if !isNil(nt) {
1108+
out = append(out, nt.Type)
11091109
}
1110-
return Nature{Type: reflect.FuncOf(
1111-
[]reflect.Type{anyType},
1112-
[]reflect.Type{out},
1113-
false,
1114-
)}
1110+
return Nature{Type: reflect.FuncOf([]reflect.Type{anyType}, out, false)}
11151111
}
11161112

11171113
func (v *checker) PointerNode(node *ast.PointerNode) Nature {

checker/nature/nature.go

+22-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var (
1313

1414
type Nature struct {
1515
Type reflect.Type
16+
Nil bool
1617
SubType SubType
1718
Func *builtin.Function
1819
Method bool
@@ -65,6 +66,12 @@ func (n Nature) Elem() Nature {
6566
}
6667

6768
func (n Nature) AssignableTo(nt Nature) bool {
69+
if n.Nil {
70+
// Untyped nil is assignable to any interface, but implements only the empty interface.
71+
if nt.Type != nil && nt.Type.Kind() == reflect.Interface {
72+
return true
73+
}
74+
}
6875
if n.Type == nil || nt.Type == nil {
6976
return false
7077
}
@@ -196,17 +203,30 @@ func (n Nature) List() map[string]Nature {
196203
}
197204
}
198205

199-
switch n.Type.Kind() {
206+
t := deref.Type(n.Type)
207+
208+
switch t.Kind() {
200209
case reflect.Struct:
201-
for name, nt := range fields(n.Type) {
210+
for name, nt := range StructFields(t) {
202211
if _, ok := table[name]; ok {
203212
continue
204213
}
205214
table[name] = nt
206215
}
207216

208217
case reflect.Map:
218+
if st, ok := n.SubType.(Map); ok {
219+
for key, nt := range st.Fields {
220+
if _, ok := table[key]; ok {
221+
continue
222+
}
223+
table[key] = nt
224+
}
225+
}
209226
v := reflect.ValueOf(n.SubType)
227+
if v.Kind() != reflect.Map {
228+
break
229+
}
210230
for _, key := range v.MapKeys() {
211231
value := v.MapIndex(key)
212232
if key.Kind() == reflect.String && value.IsValid() && value.CanInterface() {

checker/nature/types.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99

1010
func Of(value any) Nature {
1111
if value == nil {
12-
return Nature{}
12+
return Nature{Nil: true}
1313
}
1414

1515
v := reflect.ValueOf(value)
@@ -32,6 +32,10 @@ func Of(value any) Nature {
3232
case types.Map, types.StrictMap:
3333
subMap.Fields[key.String()] = Of(face)
3434
default:
35+
if face == nil {
36+
subMap.Fields[key.String()] = Nature{Nil: true}
37+
continue
38+
}
3539
subMap.Fields[key.String()] = Nature{Type: reflect.TypeOf(face)}
3640

3741
}

checker/nature/utils.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func fetchField(t reflect.Type, name string) (reflect.StructField, bool) {
4141
return reflect.StructField{}, false
4242
}
4343

44-
func fields(t reflect.Type) map[string]Nature {
44+
func StructFields(t reflect.Type) map[string]Nature {
4545
table := make(map[string]Nature)
4646

4747
t = deref.Type(t)
@@ -55,7 +55,7 @@ func fields(t reflect.Type) map[string]Nature {
5555
f := t.Field(i)
5656

5757
if f.Anonymous {
58-
for name, typ := range fields(f.Type) {
58+
for name, typ := range StructFields(f.Type) {
5959
if _, ok := table[name]; ok {
6060
continue
6161
}

checker/types.go

+3-9
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99

1010
var (
1111
unknown = Nature{}
12-
nilNature = Nature{Type: reflect.TypeOf(Nil{})}
12+
nilNature = Nature{Nil: true}
1313
boolNature = Nature{Type: reflect.TypeOf(true)}
1414
integerNature = Nature{Type: reflect.TypeOf(0)}
1515
floatNature = Nature{Type: reflect.TypeOf(float64(0))}
@@ -27,14 +27,8 @@ var (
2727
arrayType = reflect.TypeOf([]any{})
2828
)
2929

30-
// Nil is a special type to represent nil.
31-
type Nil struct{}
32-
3330
func isNil(nt Nature) bool {
34-
if nt.Type == nil {
35-
return false
36-
}
37-
return nt.Type == nilNature.Type
31+
return nt.Nil
3832
}
3933

4034
func combined(l, r Nature) Nature {
@@ -71,7 +65,7 @@ func or(l, r Nature, fns ...func(Nature) bool) bool {
7165

7266
func isUnknown(nt Nature) bool {
7367
switch {
74-
case nt.Type == nil:
68+
case nt.Type == nil && !nt.Nil:
7569
return true
7670
case nt.Kind() == reflect.Interface:
7771
return true

compiler/compiler.go

+3
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,9 @@ func (c *compiler) PairNode(node *ast.PairNode) {
11981198
}
11991199

12001200
func (c *compiler) derefInNeeded(node ast.Node) {
1201+
if node.Nature().Nil {
1202+
return
1203+
}
12011204
switch node.Type().Kind() {
12021205
case reflect.Ptr, reflect.Interface:
12031206
c.emit(OpDeref)

conf/config.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,8 @@ func (c *Config) IsOverridden(name string) bool {
8282
if _, ok := c.Functions[name]; ok {
8383
return true
8484
}
85-
// TODO: check vars
86-
//if _, ok := c.Types[name]; ok {
87-
// return true
88-
//}
85+
if _, ok := c.Env.Get(name); ok {
86+
return true
87+
}
8988
return false
9089
}

docgen/docgen.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"regexp"
66
"strings"
77

8-
"github.com/expr-lang/expr/conf"
8+
"github.com/expr-lang/expr/checker/nature"
99
"github.com/expr-lang/expr/internal/deref"
1010
)
1111

@@ -84,8 +84,8 @@ func CreateDoc(i any) *Context {
8484
PkgPath: deref.Type(reflect.TypeOf(i)).PkgPath(),
8585
}
8686

87-
for name, t := range conf.CreateTypesTable(i) {
88-
if t.Ambiguous {
87+
for name, t := range nature.Of(i).List() {
88+
if _, ok := c.Variables[Identifier(name)]; ok {
8989
continue
9090
}
9191
c.Variables[Identifier(name)] = c.use(t.Type, fromMethod(t.Method))
@@ -220,8 +220,11 @@ appendix:
220220
c.Types[name] = a
221221
}
222222

223-
for name, field := range conf.FieldsFromStruct(t) {
224-
if isPrivate(name) || isProtobuf(name) || field.Ambiguous {
223+
for name, field := range nature.StructFields(t) {
224+
if isPrivate(name) || isProtobuf(name) {
225+
continue
226+
}
227+
if _, ok := a.Fields[Identifier(name)]; ok {
225228
continue
226229
}
227230
a.Fields[Identifier(name)] = c.use(field.Type)

docgen/docgen_test.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func TestCreateDoc(t *testing.T) {
131131
PkgPath: "github.com/expr-lang/expr/docgen_test",
132132
}
133133

134-
assert.EqualValues(t, expected, doc)
134+
assert.Equal(t, expected.Markdown(), doc.Markdown())
135135
}
136136

137137
type A struct {
@@ -160,6 +160,9 @@ func TestCreateDoc_Ambiguous(t *testing.T) {
160160
Kind: "struct",
161161
Name: "A",
162162
},
163+
"AmbiguousField": {
164+
Kind: "int",
165+
},
163166
"B": {
164167
Kind: "struct",
165168
Name: "B",
@@ -189,16 +192,17 @@ func TestCreateDoc_Ambiguous(t *testing.T) {
189192
"C": {
190193
Kind: "struct",
191194
Fields: map[Identifier]*Type{
192-
"A": {Kind: "struct", Name: "A"},
193-
"B": {Kind: "struct", Name: "B"},
194-
"OkField": {Kind: "int"},
195+
"A": {Kind: "struct", Name: "A"},
196+
"AmbiguousField": {Kind: "int"},
197+
"B": {Kind: "struct", Name: "B"},
198+
"OkField": {Kind: "int"},
195199
},
196200
},
197201
},
198202
PkgPath: "github.com/expr-lang/expr/docgen_test",
199203
}
200204

201-
assert.EqualValues(t, expected, doc)
205+
assert.Equal(t, expected.Markdown(), doc.Markdown())
202206
}
203207

204208
func TestCreateDoc_FromMap(t *testing.T) {
@@ -247,7 +251,7 @@ func TestCreateDoc_FromMap(t *testing.T) {
247251
},
248252
}
249253

250-
require.EqualValues(t, expected, doc)
254+
require.EqualValues(t, expected.Markdown(), doc.Markdown())
251255
}
252256

253257
func TestContext_Markdown(t *testing.T) {

expr_test.go

-4
Original file line numberDiff line numberDiff line change
@@ -1673,10 +1673,6 @@ func TestIssue105(t *testing.T) {
16731673

16741674
_, err := expr.Compile(code, expr.Env(Env{}))
16751675
require.NoError(t, err)
1676-
1677-
_, err = expr.Compile(`Field == ''`, expr.Env(Env{}))
1678-
require.Error(t, err)
1679-
require.Contains(t, err.Error(), "ambiguous identifier Field")
16801676
}
16811677

16821678
func TestIssue_nested_closures(t *testing.T) {

0 commit comments

Comments
 (0)