Add Hash to Value interface.

This commit is contained in:
Qi Xiao 2017-08-30 20:52:27 +02:00
parent 508b41b914
commit 4f884b6dbf
23 changed files with 167 additions and 1 deletions

View File

@ -8,9 +8,11 @@ import (
"os"
"strings"
"sync"
"unsafe"
"github.com/elves/elvish/edit/ui"
"github.com/elves/elvish/eval"
"github.com/xiaq/persistent/hash"
)
// This file implements types and functions for interactions with the
@ -41,6 +43,10 @@ func (bf *BuiltinFn) Equal(a interface{}) bool {
return bf == a
}
func (bf *BuiltinFn) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(bf))
}
// Repr returns the representation of a builtin function as a variable name.
func (bf *BuiltinFn) Repr(int) string {
return "$" + bf.name

View File

@ -2,8 +2,10 @@ package edit
import (
"errors"
"unsafe"
"github.com/elves/elvish/eval"
"github.com/xiaq/persistent/hash"
)
// For an overview of completion, see the comment in completers.go.
@ -123,6 +125,10 @@ func (bac *builtinArgCompleter) Equal(a interface{}) bool {
return bac == a
}
func (bac *builtinArgCompleter) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(bac))
}
func (bac *builtinArgCompleter) Repr(int) string {
return "$edit:&" + bac.name
}

View File

@ -4,6 +4,7 @@ import (
"github.com/elves/elvish/edit/ui"
"github.com/elves/elvish/eval"
"github.com/elves/elvish/parse"
"github.com/xiaq/persistent/hash"
)
func getBinding(mode string, k ui.Key) eval.CallableValue {
@ -45,6 +46,17 @@ func (bt BindingTable) Equal(a interface{}) bool {
return true
}
func (bt BindingTable) Hash() uint32 {
h := hash.DJBInit
for k, v := range bt.inner {
// TODO(xiaq): Use a more efficient implementation to derive a hash from
// ui.Key.
h = hash.DJBCombine(h, hash.String(k.String()))
h = hash.DJBCombine(h, v.Hash())
}
return h
}
// Repr returns the representation of the binding table as if it were an
// ordinary map.
func (bt BindingTable) Repr(indent int) string {

View File

@ -8,6 +8,7 @@ import (
"github.com/elves/elvish/edit/ui"
"github.com/elves/elvish/eval"
"github.com/elves/elvish/parse"
"github.com/xiaq/persistent/hash"
)
type candidate struct {
@ -29,10 +30,11 @@ func (cs rawCandidates) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
func (cs rawCandidates) Less(i, j int) bool { return cs[i].text() < cs[j].text() }
// plainCandidate is a minimal implementation of rawCandidate.
type plainCandidate string
type plainCandidate eval.String
func (plainCandidate) Kind() string { return "string" }
func (p plainCandidate) Equal(a interface{}) bool { return p == a }
func (p plainCandidate) Hash() uint32 { return hash.String(string(p)) }
func (p plainCandidate) Repr(l int) string { return eval.String(p).Repr(l) }
func (p plainCandidate) text() string { return string(p) }
@ -47,6 +49,7 @@ type noQuoteCandidate string
func (noQuoteCandidate) Kind() string { return "string" }
func (nq noQuoteCandidate) Equal(a interface{}) bool { return nq == a }
func (nq noQuoteCandidate) Hash() uint32 { return hash.String(string(nq)) }
func (nq noQuoteCandidate) Repr(l int) string { return eval.String(nq).Repr(l) }
func (nq noQuoteCandidate) text() string { return string(nq) }
@ -71,6 +74,15 @@ func (c *complexCandidate) Equal(a interface{}) bool {
return ok && c.stem == rhs.stem && c.codeSuffix == rhs.codeSuffix && c.displaySuffix == rhs.displaySuffix && c.style.Eq(rhs.style)
}
func (c *complexCandidate) Hash() uint32 {
h := hash.DJBInit
h = hash.DJBCombine(h, hash.String(c.stem))
h = hash.DJBCombine(h, hash.String(c.codeSuffix))
h = hash.DJBCombine(h, hash.String(c.displaySuffix))
h = hash.DJBCombine(h, c.style.Hash())
return h
}
func (c *complexCandidate) Repr(indent int) string {
// TODO(xiaq): Pretty-print when indent >= 0
return fmt.Sprintf("(edit:complex-candidate %s &code-suffix=%s &display-suffix=%s style=%s)",

View File

@ -25,6 +25,12 @@ func (hv History) Equal(a interface{}) bool {
return ok
}
func (hv History) Hash() uint32 {
// TODO(xiaq): Make a global registry of singleton hashes to avoid
// collision.
return 100
}
func (hv History) Repr(int) string {
return "$le:history"
}

View File

@ -4,6 +4,7 @@ import (
"strings"
"github.com/elves/elvish/parse"
"github.com/xiaq/persistent/hash"
)
// Styled is a piece of text with style.
@ -72,6 +73,13 @@ func (s *Styled) Equal(a interface{}) bool {
return s.Text == rhs.Text && s.Styles.Eq(rhs.Styles)
}
func (s *Styled) Hash() uint32 {
h := hash.DJBInit
h = hash.DJBCombine(h, hash.String(s.Text))
h = hash.DJBCombine(h, s.Styles.Hash())
return h
}
func (s *Styled) String() string {
return "\033[" + s.Styles.String() + "m" + s.Text + "\033[m"
}
@ -94,6 +102,14 @@ func (ss Styles) Eq(rhs Styles) bool {
return true
}
func (ss Styles) Hash() uint32 {
h := hash.DJBInit
for _, s := range ss {
h = hash.DJBCombine(h, hash.String(s))
}
return h
}
func JoinStyles(so Styles, st ...Styles) Styles {
for _, v := range st {
so = append(so, v...)

View File

@ -11,6 +11,13 @@ func (b Bool) Equal(rhs interface{}) bool {
return b == rhs
}
func (b Bool) Hash() uint32 {
if b {
return 1
}
return 0
}
func (b Bool) Repr(int) string {
if b {
return "$true"

View File

@ -23,11 +23,13 @@ import (
"syscall"
"time"
"unicode/utf8"
"unsafe"
"github.com/elves/elvish/parse"
"github.com/elves/elvish/store/storedefs"
"github.com/elves/elvish/sys"
"github.com/elves/elvish/util"
"github.com/xiaq/persistent/hash"
)
var builtinFns []*BuiltinFn
@ -52,6 +54,10 @@ func (b *BuiltinFn) Equal(rhs interface{}) bool {
return b == rhs
}
func (b *BuiltinFn) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(b))
}
// Repr returns an opaque representation "<builtin xxx>".
func (b *BuiltinFn) Repr(int) string {
return "<builtin " + b.Name + ">"

View File

@ -3,6 +3,9 @@ package eval
import (
"errors"
"fmt"
"unsafe"
"github.com/xiaq/persistent/hash"
)
// ErrArityMismatch is thrown by a closure when the number of arguments the user
@ -36,6 +39,10 @@ func (c *Closure) Equal(rhs interface{}) bool {
return c == rhs
}
func (c *Closure) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(c))
}
// Repr returns an opaque representation "<closure 0x23333333>".
func (c *Closure) Repr(int) string {
return fmt.Sprintf("<closure %p>", c)

View File

@ -5,6 +5,8 @@ import (
"os"
"strings"
"sync"
"github.com/xiaq/persistent/hash"
)
// Errors
@ -70,6 +72,14 @@ func (epl *EnvPathList) Equal(a interface{}) bool {
return epl == a || eqListLike(epl, a)
}
func (epl *EnvPathList) Hash() uint32 {
h := hash.DJBInit
for _, p := range epl.get() {
h = hash.DJBCombine(h, hash.String(p))
}
return h
}
// Repr returns the representation of an EnvPathList, as if it were an ordinary
// list.
func (epl *EnvPathList) Repr(indent int) string {

View File

@ -6,9 +6,11 @@ import (
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/elves/elvish/parse"
"github.com/elves/elvish/util"
"github.com/xiaq/persistent/hash"
)
// Exception represents an elvish exception. It is both a Value accessible to
@ -76,6 +78,10 @@ func (exc *Exception) Equal(rhs interface{}) bool {
return exc == rhs
}
func (exc *Exception) Hash() uint32 {
return hash.Pointer(unsafe.Pointer(exc))
}
func (exc *Exception) Bool() bool {
return exc.Cause == nil
}

View File

@ -8,6 +8,7 @@ import (
"github.com/elves/elvish/parse"
"github.com/elves/elvish/util"
"github.com/xiaq/persistent/hash"
)
// FdNil is a special impossible fd value used for "close fd" in
@ -32,6 +33,10 @@ func (e ExternalCmd) Equal(a interface{}) bool {
return e == a
}
func (e ExternalCmd) Hash() uint32 {
return hash.String(e.Name)
}
func (e ExternalCmd) Repr(int) string {
return "<external " + parse.Quote(e.Name) + ">"
}

View File

@ -5,6 +5,7 @@ import (
"os"
"github.com/elves/elvish/parse"
"github.com/xiaq/persistent/hash"
)
type File struct {
@ -21,6 +22,10 @@ func (f File) Equal(rhs interface{}) bool {
return f == rhs
}
func (f File) Hash() uint32 {
return hash.UIntPtr(f.inner.Fd())
}
func (f File) Repr(int) string {
return fmt.Sprintf("<file{%s %p}>", parse.Quote(f.inner.Name()), f.inner)
}

View File

@ -64,6 +64,11 @@ func (gp GlobPattern) Equal(a interface{}) bool {
return reflect.DeepEqual(gp, a)
}
func (gp GlobPattern) Hash() uint32 {
// GlobPattern is not a first-class value.
return 0
}
func (gp GlobPattern) Repr(int) string {
return fmt.Sprintf("<GlobPattern%v>", gp)
}

View File

@ -50,6 +50,10 @@ func (l List) Equal(rhs interface{}) bool {
return eqListLike(l, rhs)
}
func (l List) Hash() uint32 {
return hashListLike(l)
}
func (l List) Repr(indent int) string {
var b ListReprBuilder
b.Indent = indent

View File

@ -1,5 +1,7 @@
package eval
import "github.com/xiaq/persistent/hash"
type ListLike interface {
Lener
Iterable
@ -16,3 +18,12 @@ func eqListLike(lhs ListLike, r interface{}) bool {
}
return true
}
func hashListLike(l ListLike) uint32 {
h := hash.DJBInit
l.Iterate(func(v Value) bool {
// h = hash.DJBCombine(h, v.Hash())
return true
})
return h
}

View File

@ -30,6 +30,10 @@ func (m Map) Equal(a interface{}) bool {
return m == a || eqMapLike(m, a)
}
func (m Map) Hash() uint32 {
return hashMapLike(m)
}
func (m Map) MarshalJSON() ([]byte, error) {
// XXX Not the most efficient way.
mm := map[string]Value{}

View File

@ -1,5 +1,7 @@
package eval
import "github.com/xiaq/persistent/hash"
type MapLike interface {
Lener
IndexOneer
@ -26,3 +28,13 @@ func eqMapLike(lhs MapLike, a interface{}) bool {
})
return eq
}
func hashMapLike(m MapLike) uint32 {
h := hash.DJBInit
m.IteratePair(func(k, v Value) bool {
// h = hash.DJBCombine(h, k.Hash())
// h = hash.DJBCombine(h, v.Hash())
return true
})
return h
}

View File

@ -3,6 +3,8 @@ package eval
import (
"fmt"
"os"
"github.com/xiaq/persistent/hash"
)
type Pipe struct {
@ -19,6 +21,13 @@ func (p Pipe) Equal(rhs interface{}) bool {
return p == rhs
}
func (p Pipe) Hash() uint32 {
h := hash.DJBInit
h = hash.DJBCombine(h, hash.UIntPtr(p.r.Fd()))
h = hash.DJBCombine(h, hash.UIntPtr(p.w.Fd()))
return h
}
func (p Pipe) Repr(int) string {
return fmt.Sprintf("<pipe{%v %v}>", p.r.Fd(), p.w.Fd())
}

View File

@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"math/big"
"github.com/xiaq/persistent/hash"
)
var ErrOnlyStrOrRat = errors.New("only str or rat may be converted to rat")
@ -30,6 +32,11 @@ func (r Rat) Equal(a interface{}) bool {
return r.b.Cmp(r2.b) == 0
}
func (r Rat) Hash() uint32 {
// TODO(xiaq): Use a more efficient implementation.
return hash.String(r.String())
}
func (r Rat) Repr(int) string {
return "(rat " + r.String() + ")"
}

View File

@ -4,6 +4,7 @@ import (
"unicode/utf8"
"github.com/elves/elvish/parse"
"github.com/xiaq/persistent/hash"
)
// String is just a string.
@ -26,6 +27,10 @@ func (s String) Equal(rhs interface{}) bool {
return s == rhs
}
func (s String) Hash() uint32 {
return hash.String(string(s))
}
func (s String) String() string {
return string(s)
}

View File

@ -31,6 +31,10 @@ func (s *Struct) Equal(rhs interface{}) bool {
return s == rhs || eqMapLike(s, rhs)
}
func (s *Struct) Hash() uint32 {
return hashMapLike(s)
}
func (s *Struct) Repr(indent int) string {
var builder MapReprBuilder
builder.Indent = indent

View File

@ -17,6 +17,7 @@ const (
type Value interface {
Kinder
Equaler
Hasher
Reprer
}