Made Hashable public and bindable
All checks were successful
Build / build (push) Successful in 2m34s

This commit is contained in:
Leon Mika 2025-05-17 10:34:39 +10:00
parent 27b6cc0b92
commit 109be33d14
4 changed files with 60 additions and 10 deletions

View file

@ -354,8 +354,8 @@ func objectsEqual(l, r Object) bool {
} }
} }
return true return true
case hashable: case Hashable:
rv, ok := r.(hashable) rv, ok := r.(Hashable)
if !ok { if !ok {
return false return false
} }
@ -469,7 +469,7 @@ func lenBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
return IntObject(len(string(v))), nil return IntObject(len(string(v))), nil
case Listable: case Listable:
return IntObject(v.Len()), nil return IntObject(v.Len()), nil
case hashable: case Hashable:
return IntObject(v.Len()), nil return IntObject(v.Len()), nil
case Iterable: case Iterable:
cnt := 0 cnt := 0
@ -497,10 +497,10 @@ func indexLookup(ctx context.Context, obj, elem Object) (Object, error) {
return v.Index(int(intIdx)), nil return v.Index(int(intIdx)), nil
} }
return nil, nil return nil, nil
case hashable: case Hashable:
strIdx, ok := elem.(StringObject) strIdx, ok := elem.(StringObject)
if !ok { if !ok {
return nil, errors.New("expected string for hashable") return nil, errors.New("expected string for Hashable")
} }
return v.Value(string(strIdx)), nil return v.Value(string(strIdx)), nil
} }
@ -531,7 +531,7 @@ func keysBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
val := args.args[0] val := args.args[0]
switch v := val.(type) { switch v := val.(type) {
case hashable: case Hashable:
keys := make(ListObject, 0, v.Len()) keys := make(ListObject, 0, v.Len())
if err := v.Each(func(k string, _ Object) error { if err := v.Each(func(k string, _ Object) error {
keys = append(keys, StringObject(k)) keys = append(keys, StringObject(k))
@ -676,7 +676,7 @@ func filterBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
} }
} }
return &newList, nil return &newList, nil
case hashable: case Hashable:
newHash := hashObject{} newHash := hashObject{}
if err := t.Each(func(k string, v Object) error { if err := t.Each(func(k string, v Object) error {
if m, err := inv.invoke(ctx, args.fork([]Object{StringObject(k), v})); err != nil { if m, err := inv.invoke(ctx, args.fork([]Object{StringObject(k), v})); err != nil {
@ -741,7 +741,7 @@ func reduceBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
accum = newAccum accum = newAccum
} }
return accum, nil return accum, nil
case hashable: case Hashable:
// TODO: should raise error? // TODO: should raise error?
if err := t.Each(func(k string, v Object) error { if err := t.Each(func(k string, v Object) error {
newAccum, err := block.invoke(ctx, args.fork([]Object{StringObject(k), v, accum})) newAccum, err := block.invoke(ctx, args.fork([]Object{StringObject(k), v, accum}))
@ -942,7 +942,7 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (Object, error) {
} }
} }
} }
case hashable: case Hashable:
err := t.Each(func(k string, v Object) error { err := t.Each(func(k string, v Object) error {
last, err = args.evalBlock(ctx, blockIdx, []Object{StringObject(k), v}, false) last, err = args.evalBlock(ctx, blockIdx, []Object{StringObject(k), v}, false)
return err return err

View file

@ -40,7 +40,7 @@ type ModListable interface {
Insert(idx int, obj Object) error Insert(idx int, obj Object) error
} }
type hashable interface { type Hashable interface {
Len() int Len() int
Value(k string) Object Value(k string) Object
Each(func(k string, v Object) error) error Each(func(k string, v Object) error) error

View file

@ -127,6 +127,13 @@ func (ca CallArgs) bindArg(v interface{}, arg Object) error {
return nil return nil
} }
return errors.New("exepected listable") return errors.New("exepected listable")
case *Hashable:
i, ok := arg.(Hashable)
if !ok {
return errors.New("exepected hashable")
}
*t = i
return nil
case *Iterable: case *Iterable:
if i, ok := arg.(Iterable); ok { if i, ok := arg.(Iterable); ok {
*t = i *t = i

View file

@ -301,6 +301,49 @@ func TestCallArgs_CanBind(t *testing.T) {
}) })
} }
t.Run("can bind Hashable", func(t *testing.T) {
tests := []struct {
descr string
eval string
want any
wantErr bool
}{
{descr: "return key 1", eval: `keyval [a:"hello" b:"world"] "a"`, want: "hello"},
{descr: "return key 2", eval: `keyval [a:"hello" b:"world"] "b"`, want: "world"},
{descr: "return key 3", eval: `keyval (keyval [a:"hello" b:[c:"fla"]] "b") c`, want: "fla"},
{descr: "err 1", eval: `keyval not-a-hashable "b"`, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.descr, func(t *testing.T) {
ctx := context.Background()
inst := ucl.New()
inst.SetBuiltin("keyval", func(ctx context.Context, args ucl.CallArgs) (any, error) {
var (
h ucl.Hashable
k string
)
if err := args.Bind(&h, &k); err != nil {
return nil, err
}
return h.Value(k), nil
})
res, err := inst.Eval(ctx, tt.eval)
if tt.wantErr {
assert.Error(t, err)
assert.Nil(t, res)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.want, res)
}
})
}
})
t.Run("can bind invokable", func(t *testing.T) { t.Run("can bind invokable", func(t *testing.T) {
inst := ucl.New() inst := ucl.New()
inst.SetBuiltin("toUpper", func(ctx context.Context, args ucl.CallArgs) (any, error) { inst.SetBuiltin("toUpper", func(ctx context.Context, args ucl.CallArgs) (any, error) {