eval/vals: Simplify the handling of lists and maps.

Also introduce type aliases List and Map to save some typing.
This commit is contained in:
Qi Xiao 2019-04-19 12:24:45 +01:00
parent fe6fc5b89a
commit 8b3ca8bd02
12 changed files with 45 additions and 109 deletions

View File

@ -2,8 +2,6 @@ package vals
import (
"errors"
"github.com/xiaq/persistent/vector"
)
// Assocer wraps the Assoc method.
@ -21,15 +19,15 @@ var (
// Assoc takes a container, a key and value, and returns a modified version of
// the container, in which the key associated with the value. It is implemented
// for the builtin type string, and types satisfying the listAssocable,
// mapAssocable or Assocer interface. For other types, it returns an error.
// for the builtin type string, and List and Map types, and types satisfying the
// Assocer interface. For other types, it returns an error.
func Assoc(a, k, v interface{}) (interface{}, error) {
switch a := a.(type) {
case string:
return assocString(a, k, v)
case listAssocable:
case List:
return assocList(a, k, v)
case mapAssocable:
case Map:
return a.Assoc(k, v), nil
case Assocer:
return a.Assoc(k, v)
@ -49,14 +47,7 @@ func assocString(s string, k, v interface{}) (interface{}, error) {
return s[:i] + repl + s[j:], nil
}
type listAssocable interface {
Lener
Assoc(int, interface{}) vector.Vector
}
var _ listAssocable = vector.Vector(nil)
func assocList(l listAssocable, k, v interface{}) (interface{}, error) {
func assocList(l List, k, v interface{}) (interface{}, error) {
kstring, ok := k.(string)
if !ok {
return nil, errIndexMustBeString

View File

@ -9,11 +9,11 @@ type Dissocer interface {
// Dissoc takes a container and a key, and returns a modified version of the
// container, with the given key dissociated with any value. It is implemented
// for types satisfying the mapDissocable or Dissocer interface. For other
// for the Map type and types satisfying the Dissocer interface. For other
// types, it returns nil.
func Dissoc(a, k interface{}) interface{} {
switch a := a.(type) {
case mapDissocable:
case Map:
return a.Dissoc(k)
case Dissocer:
return a.Dissoc(k)

View File

@ -2,8 +2,6 @@ package vals
import (
"reflect"
"github.com/xiaq/persistent/hashmap"
)
// Equaler wraps the Equal method.
@ -14,9 +12,9 @@ type Equaler interface {
}
// Equal returns whether two values are equal. It is implemented for the builtin
// types bool and string, and types satisfying the listEqualable, mapEqualable
// or Equaler interface. For other types, it uses reflect.DeepEqual to compare
// the two values.
// types bool and string, the List and Map types, and types implementing the
// Equaler interface. For other types, it uses reflect.DeepEqual to compare the
// two values.
func Equal(x, y interface{}) bool {
switch x := x.(type) {
case nil:
@ -27,13 +25,13 @@ func Equal(x, y interface{}) bool {
return x == y
case string:
return x == y
case listEqualable:
if yy, ok := y.(listEqualable); ok {
case List:
if yy, ok := y.(List); ok {
return equalList(x, yy)
}
return false
case mapEqualable:
if yy, ok := y.(mapEqualable); ok {
case Map:
if yy, ok := y.(Map); ok {
return equalMap(x, yy)
}
return false
@ -44,12 +42,7 @@ func Equal(x, y interface{}) bool {
}
}
type listEqualable interface {
Lener
listIterable
}
func equalList(x, y listEqualable) bool {
func equalList(x, y List) bool {
if x.Len() != y.Len() {
return false
}
@ -65,15 +58,7 @@ func equalList(x, y listEqualable) bool {
return true
}
type mapEqualable interface {
Lener
Index(interface{}) (interface{}, bool)
Iterator() hashmap.Iterator
}
var _ mapEqualable = hashmap.Map(nil)
func equalMap(x, y mapEqualable) bool {
func equalMap(x, y Map) bool {
if x.Len() != y.Len() {
return false
}

View File

@ -11,7 +11,7 @@ type Hasher interface {
}
// Hash returns the 32-bit hash of a value. It is implemented for the builtin
// types bool and string, and types satisfying the listIterable, mapIterable or
// types bool and string, the List and Map types, and types satisfying the
// Hasher interface. For other values, it returns 0 (which is OK in terms of
// correctness).
func Hash(v interface{}) uint32 {
@ -23,13 +23,13 @@ func Hash(v interface{}) uint32 {
return 0
case string:
return hash.String(v)
case listIterable:
case List:
h := hash.DJBInit
for it := v.Iterator(); it.HasElem(); it.Next() {
h = hash.DJBCombine(h, Hash(it.Elem()))
}
return h
case mapIterable:
case Map:
h := hash.DJBInit
for it := v.Iterator(); it.HasElem(); it.Next() {
k, v := it.Elem()

View File

@ -34,13 +34,14 @@ func (err noSuchKeyError) Error() string {
}
// Index indexes a value with the given key. It is implemented for the builtin
// string type, and types satisfying the listIndexable, ErrIndexer or Indexer
// interface. For other types, it returns a nil value and a non-nil error.
// string type, the List type, and types satisfying the ErrIndexer or Indexer
// interface (the Map type implements Indexer). For other types, it returns a
// nil value and a non-nil error.
func Index(a, k interface{}) (interface{}, error) {
switch a := a.(type) {
case string:
return indexString(a, k)
case listIndexable:
case List:
return indexList(a, k)
case ErrIndexer:
return a.Index(k)

View File

@ -4,8 +4,6 @@ import (
"errors"
"strconv"
"strings"
"github.com/xiaq/persistent/vector"
)
var (
@ -14,15 +12,7 @@ var (
errIndexOutOfRange = errors.New("index out of range")
)
type listIndexable interface {
Lener
Index(int) (interface{}, bool)
SubVector(int, int) vector.Vector
}
var _ listIndexable = vector.Vector(nil)
func indexList(l listIndexable, rawIndex interface{}) (interface{}, error) {
func indexList(l List, rawIndex interface{}) (interface{}, error) {
index, err := ConvertListIndex(rawIndex, l.Len())
if err != nil {
return nil, err

View File

@ -2,8 +2,6 @@ package vals
import (
"errors"
"github.com/xiaq/persistent/vector"
)
// Iterator wraps the Iterate method.
@ -17,7 +15,7 @@ type Iterator interface {
// true, calling Iterate(v, f) will not result in an error.
func CanIterate(v interface{}) bool {
switch v.(type) {
case Iterator, string, listIterable:
case Iterator, string, List:
return true
}
return false
@ -25,9 +23,9 @@ func CanIterate(v interface{}) bool {
// Iterate iterates the supplied value, and calls the supplied function in each
// of its elements. The function can return false to break the iteration. It is
// implemented for the builtin type string, and types satisfying the
// listIterable or Iterator interface. For these types, it always returns a nil
// error. For other types, it doesn't do anything and returns an error.
// implemented for the builtin type string, the List type, and types satisfying
// the Iterator interface. For these types, it always returns a nil error. For
// other types, it doesn't do anything and returns an error.
func Iterate(v interface{}, f func(interface{}) bool) error {
switch v := v.(type) {
case string:
@ -37,7 +35,7 @@ func Iterate(v interface{}, f func(interface{}) bool) error {
break
}
}
case listIterable:
case List:
for it := v.Iterator(); it.HasElem(); it.Next() {
if !f(it.Elem()) {
break
@ -51,12 +49,6 @@ func Iterate(v interface{}, f func(interface{}) bool) error {
return nil
}
type listIterable interface {
Iterator() vector.Iterator
}
var _ listIterable = vector.Vector(nil)
// Collect collects all elements of an iterable value into a slice.
func Collect(it interface{}) ([]interface{}, error) {
var vs []interface{}

View File

@ -2,8 +2,6 @@ package vals
import (
"errors"
"github.com/xiaq/persistent/hashmap"
)
// KeysIterator wraps the IterateKeys method.
@ -15,12 +13,12 @@ type KeysIterator interface {
// IterateKeys iterates the keys of the supplied value, calling the supplied
// function for each key. The function can return false to break the iteration.
// It is implemented for the mapKeysIterable type and types satisfying the
// IterateKeyser interface. For these types, it always returns a nil error. For
// other types, it doesn't do anything and returns an error.
// It is implemented for the Map type and types satisfying the IterateKeyser
// interface. For these types, it always returns a nil error. For other types,
// it doesn't do anything and returns an error.
func IterateKeys(v interface{}, f func(interface{}) bool) error {
switch v := v.(type) {
case mapKeysIterable:
case Map:
for it := v.Iterator(); it.HasElem(); it.Next() {
k, _ := it.Elem()
if !f(k) {
@ -34,7 +32,3 @@ func IterateKeys(v interface{}, f func(interface{}) bool) error {
}
return nil
}
type mapKeysIterable interface {
Iterator() hashmap.Iterator
}

View File

@ -2,9 +2,6 @@ package vals
import (
"fmt"
"github.com/xiaq/persistent/hashmap"
"github.com/xiaq/persistent/vector"
)
// Kinder wraps the Kind method.
@ -14,7 +11,7 @@ type Kinder interface {
// Kind returns the "kind" of the value, a concept similar to type but not yet
// very well defined. It is implemented for the builtin nil, bool and string,
// the Vector and Map types, and types implementing the Kinder interface. For
// the List and Map types, and types implementing the Kinder interface. For
// other types, it returns the Go type name of the argument preceded by "!!".
func Kind(v interface{}) string {
switch v := v.(type) {
@ -24,9 +21,9 @@ func Kind(v interface{}) string {
return "bool"
case string:
return "string"
case vector.Vector:
case List:
return "list"
case hashmap.Map:
case Map:
return "map"
case Kinder:
return v.Kind()

View File

@ -7,6 +7,9 @@ import (
"github.com/xiaq/persistent/vector"
)
// List is an alias for the underlying type used for lists in Elvish.
type List = vector.Vector
// EmptyList is an empty list.
var EmptyList = vector.Empty

View File

@ -4,22 +4,8 @@ import (
"github.com/xiaq/persistent/hashmap"
)
type mapIterable interface {
Iterator() hashmap.Iterator
}
type mapAssocable interface {
Assoc(k, v interface{}) hashmap.Map
}
type mapDissocable interface {
Dissoc(interface{}) hashmap.Map
}
var (
_ mapIterable = hashmap.Map(nil)
_ Indexer = hashmap.Map(nil)
_ mapAssocable = hashmap.Map(nil)
_ mapDissocable = hashmap.Map(nil)
)
// Map is an alias for the underlying type used for maps in Elvish.
type Map = hashmap.Map
// EmptyMap is an empty map.
var EmptyMap = hashmap.New(Equal, Hash)

View File

@ -27,8 +27,8 @@ type Reprer interface {
// Repr returns the representation for a value, a string that is preferably (but
// not necessarily) an Elvish expression that evaluates to the argument. If
// indent >= 0, the representation is pretty-printed. It is implemented for the
// builtin types nil, bool and string, and types satisfying the listReprable,
// mapReprable or Reprer interface. For other types, it uses fmt.Sprint with the
// builtin types nil, bool and string, the List and Map and types, and types
// satisfying the Reprer interface. For other types, it uses fmt.Sprint with the
// format "<unknown %v>".
func Repr(v interface{}, indent int) string {
switch v := v.(type) {
@ -43,13 +43,13 @@ func Repr(v interface{}, indent int) string {
return parse.Quote(v)
case float64:
return fmt.Sprintf("(float64 %g)", v)
case listReprable:
case List:
b := ListReprBuilder{Indent: indent}
for it := v.Iterator(); it.HasElem(); it.Next() {
b.WriteElem(Repr(it.Elem(), indent+1))
}
return b.String()
case mapReprable:
case Map:
builder := MapReprBuilder{}
builder.Indent = indent
for it := v.Iterator(); it.HasElem(); it.Next() {
@ -63,6 +63,3 @@ func Repr(v interface{}, indent int) string {
return fmt.Sprintf("<unknown %v>", v)
}
}
type listReprable listIterable
type mapReprable mapIterable