From c72da7bf4db186bd6ca94ca58dc8da83d3bf3495 Mon Sep 17 00:00:00 2001 From: Anton Medvedev Date: Wed, 28 Aug 2024 22:47:47 +0200 Subject: [PATCH] Drop types.StrictMap and add types.Extra to define additional keys --- checker/checker_test.go | 11 +++++---- conf/env.go | 6 ----- expr_test.go | 4 ++-- types/types.go | 53 +++++++---------------------------------- types/types_test.go | 8 +------ 5 files changed, 18 insertions(+), 64 deletions(-) diff --git a/checker/checker_test.go b/checker/checker_test.go index fad56eb2..602fe4b7 100644 --- a/checker/checker_test.go +++ b/checker/checker_test.go @@ -1072,14 +1072,16 @@ func TestCheck_builtin_without_call(t *testing.T) { func TestCheck_types(t *testing.T) { env := types.Map{ - "foo": types.StrictMap{ + "foo": types.Map{ "bar": types.Map{ - "baz": types.String, + "baz": types.String, + types.Extra: types.String, }, }, - "arr": types.Array(types.StrictMap{ + "arr": types.Array(types.Map{ "value": types.String, }), + types.Extra: types.Any, } noerr := "no error" @@ -1102,7 +1104,8 @@ func TestCheck_types(t *testing.T) { tree, err := parser.Parse(test.code) require.NoError(t, err) - _, err = checker.Check(tree, conf.New(env)) + config := conf.New(env) + _, err = checker.Check(tree, config) if test.err == noerr { require.NoError(t, err) } else { diff --git a/conf/env.go b/conf/env.go index 82e6a93d..494bb5b7 100644 --- a/conf/env.go +++ b/conf/env.go @@ -20,9 +20,6 @@ func Env(env any) Nature { switch env := env.(type) { case types.Map: return env.Nature() - - case types.StrictMap: - return env.Nature() } v := reflect.ValueOf(env) @@ -53,9 +50,6 @@ func Env(env any) Nature { case types.Map: n.Fields[key.String()] = face.(types.Map).Nature() - case types.StrictMap: - n.Fields[key.String()] = face.(types.StrictMap).Nature() - default: if face == nil { n.Fields[key.String()] = Nature{Nil: true} diff --git a/expr_test.go b/expr_test.go index 4f2f829d..ced182a9 100644 --- a/expr_test.go +++ b/expr_test.go @@ -2704,7 +2704,7 @@ func TestExpr_nil_op_str(t *testing.T) { func TestExpr_env_types_map(t *testing.T) { envTypes := types.Map{ - "foo": types.StrictMap{ + "foo": types.Map{ "bar": types.String, }, } @@ -2725,7 +2725,7 @@ func TestExpr_env_types_map(t *testing.T) { func TestExpr_env_types_map_error(t *testing.T) { envTypes := types.Map{ - "foo": types.StrictMap{ + "foo": types.Map{ "bar": types.String, }, } diff --git a/types/types.go b/types/types.go index 92923f9a..d10d2fbc 100644 --- a/types/types.go +++ b/types/types.go @@ -94,16 +94,22 @@ 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. +// Map represents a map[string]any type with defined keys. type Map map[string]Type +const Extra = "[[__extra_keys__]]" + func (m Map) Nature() Nature { nt := Nature{ Type: reflect.TypeOf(map[string]any{}), Fields: make(map[string]Nature, len(m)), + Strict: true, } for k, v := range m { + if k == Extra { + nt.Strict = false + continue + } nt.Fields[k] = v.Nature() } return nt @@ -136,49 +142,6 @@ func (m Map) String() 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 - -func (m StrictMap) Nature() Nature { - nt := Nature{ - Type: reflect.TypeOf(map[string]any{}), - Fields: make(map[string]Nature, len(m)), - Strict: true, - } - for k, v := range m { - nt.Fields[k] = v.Nature() - } - return nt -} - -func (m StrictMap) Equal(t Type) bool { - if t == Any { - return true - } - 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} diff --git a/types/types_test.go b/types/types_test.go index eacfef22..15a6ca77 100644 --- a/types/types_test.go +++ b/types/types_test.go @@ -9,7 +9,7 @@ import ( func TestType_Equal(t *testing.T) { tests := []struct { - index string + index string // Index added for IDEA to show green test marker per test. a, b Type want bool }{ @@ -22,15 +22,11 @@ func TestType_Equal(t *testing.T) { {"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}, {"21", Any, Any, true}, @@ -38,8 +34,6 @@ func TestType_Equal(t *testing.T) { {"23", Int, Any, true}, {"24", Any, Map{"foo": Int}, true}, {"25", Map{"foo": Int}, Any, true}, - {"26", Any, StrictMap{"foo": Int}, true}, - {"27", StrictMap{"foo": Int}, Any, true}, {"28", Any, Array(Int), true}, {"29", Array(Int), Any, true}, }