Fixed field export

This commit is contained in:
Leon Mika 2024-04-24 21:29:26 +10:00
parent 43098fa227
commit ddd5ab74f6
4 changed files with 92 additions and 7 deletions

View file

@ -113,6 +113,23 @@ func callBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return inv.invoke(ctx, args.shift(1))
}
func lenBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
switch v := args.args[0].(type) {
case strObject:
return intObject(len(string(v))), nil
case listable:
return intObject(v.Len()), nil
case hashable:
return intObject(v.Len()), nil
}
return intObject(0), nil
}
func indexBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err

View file

@ -31,6 +31,7 @@ func New(opts ...InstOption) *Inst {
rootEC.addCmd("set", invokableFunc(setBuiltin))
rootEC.addCmd("toUpper", invokableFunc(toUpperBuiltin))
//rootEC.addCmd("cat", invokableFunc(catBuiltin))
rootEC.addCmd("len", invokableFunc(lenBuiltin))
rootEC.addCmd("index", invokableFunc(indexBuiltin))
rootEC.addCmd("call", invokableFunc(callBuiltin))

View file

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/lmika/gopkgs/fp/slices"
"reflect"
"strconv"
)
@ -160,7 +161,7 @@ func fromGoValue(v any) (object, error) {
case reflect.Slice:
return listableProxyObject{resVal}, nil
case reflect.Struct:
return structProxyObject{resVal}, nil
return newStructProxyObject(resVal), nil
}
return proxyObject{v}, nil
@ -393,6 +394,14 @@ func (p listableProxyObject) Index(i int) object {
type structProxyObject struct {
v reflect.Value
vf []reflect.StructField
}
func newStructProxyObject(v reflect.Value) structProxyObject {
return structProxyObject{
v: v,
vf: slices.Filter(reflect.VisibleFields(v.Type()), func(t reflect.StructField) bool { return t.IsExported() }),
}
}
func (s structProxyObject) String() string {
@ -404,7 +413,7 @@ func (s structProxyObject) Truthy() bool {
}
func (s structProxyObject) Len() int {
return s.v.Type().NumField()
return len(s.vf)
}
func (s structProxyObject) Value(k string) object {
@ -416,14 +425,13 @@ func (s structProxyObject) Value(k string) object {
}
func (s structProxyObject) Each(fn func(k string, v object) error) error {
for i := 0; i < s.v.Type().NumField(); i++ {
f := s.v.Type().Field(i).Name
v, err := fromGoValue(s.v.Field(i).Interface())
for _, f := range s.vf {
v, err := fromGoValue(s.v.FieldByName(f.Name).Interface())
if err != nil {
v = nil
}
if err := fn(f, v); err != nil {
if err := fn(f.Name, v); err != nil {
return err
}
}

View file

@ -386,3 +386,62 @@ func TestBuiltins_Index(t *testing.T) {
})
}
}
func TestBuiltins_Len(t *testing.T) {
tests := []struct {
desc string
expr string
want string
}{
{desc: "len of list 1", expr: `len ["alpha" "beta" "gamma"]`, want: "3\n"},
{desc: "len of list 2", expr: `len ["alpha"]`, want: "1\n"},
{desc: "len of list 3", expr: `len []`, want: "0\n"},
{desc: "len of hash 1", expr: `len ["first":"alpha" "second":"beta" "third":"gamma"]`, want: "3\n"},
{desc: "len of hash 2", expr: `len ["first":"alpha" "second":"beta"]`, want: "2\n"},
{desc: "len of hash 3", expr: `len ["first":"alpha"]`, want: "1\n"},
{desc: "len of hash 4", expr: `len [:]`, want: "0\n"},
{desc: "len of string 1", expr: `len "Hello, world"`, want: "12\n"},
{desc: "len of string 2", expr: `len "chair"`, want: "5\n"},
{desc: "len of string 3", expr: `len ""`, want: "0\n"},
{desc: "len of int", expr: `len 1232`, want: "0\n"},
{desc: "len of nil", expr: `len ()`, want: "0\n"},
{desc: "go list 1", expr: `goInt | len`, want: "3\n"},
{desc: "go struct 1", expr: `goStruct | len`, want: "3\n"},
{desc: "go struct 2", expr: `index (goStruct) Gamma | len`, want: "2\n"},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
ctx := context.Background()
outW := bytes.NewBuffer(nil)
inst := New(WithOut(outW), WithTestBuiltin())
inst.SetBuiltin("goInt", func(ctx context.Context, args CallArgs) (any, error) {
return []int{6, 5, 4}, nil
})
inst.SetBuiltin("goStruct", func(ctx context.Context, args CallArgs) (any, error) {
return struct {
Alpha string
Beta string
Gamma []int
hidden string
missing string
}{
Alpha: "foo",
Beta: "bar",
Gamma: []int{22, 33},
hidden: "hidden",
missing: "missing",
}, nil
})
err := inst.EvalAndDisplay(ctx, tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, outW.String())
})
}
}