Compare commits
No commits in common. "6cd3e9ff0fa3ce39b0283fd987a3679d76eec876" and "27463f33c37f9cab1c377d1c6669c0441f2a092c" have entirely different histories.
6cd3e9ff0f
...
27463f33c3
|
@ -40,8 +40,8 @@ type astListOrHash struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type astBlock struct {
|
type astBlock struct {
|
||||||
Names []string `parser:"LC NL* (PIPE @Ident+ PIPE NL*)?"`
|
Names []string `parser:"LC NL? (PIPE @Ident+ PIPE NL?)?"`
|
||||||
Statements []*astStatements `parser:"@@? NL* RC"`
|
Statements []*astStatements `parser:"@@ NL? RC"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type astMaybeSub struct {
|
type astMaybeSub struct {
|
||||||
|
|
50
ucl/objs.go
50
ucl/objs.go
|
@ -110,8 +110,6 @@ func (b boolObject) Truthy() bool {
|
||||||
|
|
||||||
func toGoValue(obj object) (interface{}, bool) {
|
func toGoValue(obj object) (interface{}, bool) {
|
||||||
switch v := obj.(type) {
|
switch v := obj.(type) {
|
||||||
case OpaqueObject:
|
|
||||||
return v.v, true
|
|
||||||
case nil:
|
case nil:
|
||||||
return nil, true
|
return nil, true
|
||||||
case strObject:
|
case strObject:
|
||||||
|
@ -141,9 +139,9 @@ func toGoValue(obj object) (interface{}, bool) {
|
||||||
case proxyObject:
|
case proxyObject:
|
||||||
return v.p, true
|
return v.p, true
|
||||||
case listableProxyObject:
|
case listableProxyObject:
|
||||||
return v.orig.Interface(), true
|
return v.v.Interface(), true
|
||||||
case structProxyObject:
|
case structProxyObject:
|
||||||
return v.orig.Interface(), true
|
return v.v.Interface(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, false
|
return nil, false
|
||||||
|
@ -151,8 +149,6 @@ func toGoValue(obj object) (interface{}, bool) {
|
||||||
|
|
||||||
func fromGoValue(v any) (object, error) {
|
func fromGoValue(v any) (object, error) {
|
||||||
switch t := v.(type) {
|
switch t := v.(type) {
|
||||||
case OpaqueObject:
|
|
||||||
return t, nil
|
|
||||||
case nil:
|
case nil:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case string:
|
case string:
|
||||||
|
@ -171,17 +167,10 @@ func fromGoReflectValue(resVal reflect.Value) (object, error) {
|
||||||
|
|
||||||
switch resVal.Kind() {
|
switch resVal.Kind() {
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
return listableProxyObject{v: resVal, orig: resVal}, nil
|
return listableProxyObject{resVal}, nil
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return newStructProxyObject(resVal, resVal), nil
|
return newStructProxyObject(resVal), nil
|
||||||
case reflect.Pointer:
|
case reflect.Pointer:
|
||||||
switch resVal.Elem().Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
return listableProxyObject{v: resVal.Elem(), orig: resVal}, nil
|
|
||||||
case reflect.Struct:
|
|
||||||
return newStructProxyObject(resVal.Elem(), resVal), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fromGoReflectValue(resVal.Elem())
|
return fromGoReflectValue(resVal.Elem())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,8 +407,7 @@ func (p proxyObject) Truthy() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type listableProxyObject struct {
|
type listableProxyObject struct {
|
||||||
v reflect.Value
|
v reflect.Value
|
||||||
orig reflect.Value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p listableProxyObject) String() string {
|
func (p listableProxyObject) String() string {
|
||||||
|
@ -443,16 +431,14 @@ func (p listableProxyObject) Index(i int) object {
|
||||||
}
|
}
|
||||||
|
|
||||||
type structProxyObject struct {
|
type structProxyObject struct {
|
||||||
v reflect.Value
|
v reflect.Value
|
||||||
orig reflect.Value
|
vf []reflect.StructField
|
||||||
vf []reflect.StructField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStructProxyObject(v reflect.Value, orig reflect.Value) structProxyObject {
|
func newStructProxyObject(v reflect.Value) structProxyObject {
|
||||||
return structProxyObject{
|
return structProxyObject{
|
||||||
v: v,
|
v: v,
|
||||||
orig: orig,
|
vf: slices.Filter(reflect.VisibleFields(v.Type()), func(t reflect.StructField) bool { return t.IsExported() }),
|
||||||
vf: slices.Filter(reflect.VisibleFields(v.Type()), func(t reflect.StructField) bool { return t.IsExported() }),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,22 +488,6 @@ func (s structProxyObject) Each(fn func(k string, v object) error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpaqueObject struct {
|
|
||||||
v any
|
|
||||||
}
|
|
||||||
|
|
||||||
func Opaque(v any) OpaqueObject {
|
|
||||||
return OpaqueObject{v: v}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p OpaqueObject) String() string {
|
|
||||||
return fmt.Sprintf("opaque{%T}", p.v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p OpaqueObject) Truthy() bool {
|
|
||||||
return p.v != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type errBreak struct {
|
type errBreak struct {
|
||||||
isCont bool
|
isCont bool
|
||||||
ret object
|
ret object
|
||||||
|
|
|
@ -394,36 +394,6 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
expr string
|
expr string
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
// syntax tests
|
|
||||||
{desc: "empty proc 1", expr: `
|
|
||||||
proc greet {}
|
|
||||||
greet
|
|
||||||
`, want: "(nil)\n"},
|
|
||||||
{desc: "empty proc 2", expr: `
|
|
||||||
proc greet {
|
|
||||||
}
|
|
||||||
|
|
||||||
greet
|
|
||||||
`, want: "(nil)\n"},
|
|
||||||
{desc: "empty proc 3", expr: `
|
|
||||||
proc greet {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
greet
|
|
||||||
`, want: "(nil)\n"},
|
|
||||||
{desc: "empty proc 4", expr: `
|
|
||||||
proc greet {
|
|
||||||
# bla
|
|
||||||
|
|
||||||
# di
|
|
||||||
# bla!
|
|
||||||
}
|
|
||||||
|
|
||||||
greet
|
|
||||||
`, want: "(nil)\n"},
|
|
||||||
|
|
||||||
{desc: "nil return", expr: `
|
{desc: "nil return", expr: `
|
||||||
proc greet {
|
proc greet {
|
||||||
echo "Hello"
|
echo "Hello"
|
||||||
|
@ -433,27 +403,6 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
|
|
||||||
greet
|
greet
|
||||||
`, want: "Hello\n(nil)\n"},
|
`, want: "Hello\n(nil)\n"},
|
||||||
|
|
||||||
{desc: "simple arg 1", expr: `
|
|
||||||
proc greet { |x|
|
|
||||||
return (cat "Hello, " $x)
|
|
||||||
}
|
|
||||||
|
|
||||||
greet "person"
|
|
||||||
`, want: "Hello, person\n"},
|
|
||||||
{desc: "simple arg 2", expr: `
|
|
||||||
proc greet {
|
|
||||||
# This will greet someone
|
|
||||||
# here are the args:
|
|
||||||
|x|
|
|
||||||
|
|
||||||
# And here is the code
|
|
||||||
return (cat "Hello, " $x)
|
|
||||||
}
|
|
||||||
|
|
||||||
greet "person"
|
|
||||||
`, want: "Hello, person\n"},
|
|
||||||
|
|
||||||
{desc: "simple return", expr: `
|
{desc: "simple return", expr: `
|
||||||
proc greet {
|
proc greet {
|
||||||
return "Hello, world"
|
return "Hello, world"
|
||||||
|
@ -462,7 +411,6 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
|
|
||||||
greet
|
greet
|
||||||
`, want: "Hello, world\n"},
|
`, want: "Hello, world\n"},
|
||||||
|
|
||||||
{desc: "only return current frame", expr: `
|
{desc: "only return current frame", expr: `
|
||||||
proc greetWhat {
|
proc greetWhat {
|
||||||
echo "Greet the"
|
echo "Greet the"
|
||||||
|
|
|
@ -125,23 +125,6 @@ func (ca CallArgs) bindArg(v interface{}, arg object) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := arg.(type) {
|
switch t := arg.(type) {
|
||||||
case OpaqueObject:
|
|
||||||
if v == nil {
|
|
||||||
return errors.New("opaque object not bindable to nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
vr := reflect.ValueOf(v)
|
|
||||||
tr := reflect.ValueOf(t.v)
|
|
||||||
if vr.Kind() != reflect.Pointer {
|
|
||||||
return errors.New("expected pointer for an opaque object bind")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tr.Type().AssignableTo(vr.Elem().Type()) {
|
|
||||||
return errors.New("opaque object not assignable to passed in value")
|
|
||||||
}
|
|
||||||
|
|
||||||
vr.Elem().Set(tr)
|
|
||||||
return nil
|
|
||||||
case proxyObject:
|
case proxyObject:
|
||||||
return bindProxyObject(v, reflect.ValueOf(t.p))
|
return bindProxyObject(v, reflect.ValueOf(t.p))
|
||||||
case listableProxyObject:
|
case listableProxyObject:
|
||||||
|
@ -163,18 +146,6 @@ func canBindArg(v interface{}, arg object) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := arg.(type) {
|
switch t := arg.(type) {
|
||||||
case OpaqueObject:
|
|
||||||
vr := reflect.ValueOf(v)
|
|
||||||
tr := reflect.ValueOf(t.v)
|
|
||||||
if vr.Kind() != reflect.Pointer {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tr.Type().AssignableTo(vr.Elem().Type()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
case proxyObject:
|
case proxyObject:
|
||||||
return canBindProxyObject(v, reflect.ValueOf(t.p))
|
return canBindProxyObject(v, reflect.ValueOf(t.p))
|
||||||
case listableProxyObject:
|
case listableProxyObject:
|
||||||
|
|
|
@ -3,7 +3,6 @@ package ucl_test
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -115,27 +114,6 @@ func TestInst_SetBuiltin(t *testing.T) {
|
||||||
assert.Equal(t, pair{"Hello", "World"}, res)
|
assert.Equal(t, pair{"Hello", "World"}, res)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("builtin return proxy object ptr", func(t *testing.T) {
|
|
||||||
type pair struct {
|
|
||||||
x, y string
|
|
||||||
}
|
|
||||||
|
|
||||||
inst := ucl.New()
|
|
||||||
inst.SetBuiltin("add2", func(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
||||||
var x, y string
|
|
||||||
|
|
||||||
if err := args.Bind(&x, &y); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &pair{x, y}, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
res, err := inst.Eval(context.Background(), `add2 "Hello" "World"`)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &pair{"Hello", "World"}, res)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("builtin operating on and returning proxy object", func(t *testing.T) {
|
t.Run("builtin operating on and returning proxy object", func(t *testing.T) {
|
||||||
type pair struct {
|
type pair struct {
|
||||||
x, y string
|
x, y string
|
||||||
|
@ -206,113 +184,6 @@ func TestInst_SetBuiltin(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("opaques returned as is", func(t *testing.T) {
|
|
||||||
type opaqueThingType struct {
|
|
||||||
x string
|
|
||||||
y string
|
|
||||||
z string
|
|
||||||
}
|
|
||||||
opaqueThing := &opaqueThingType{x: "do", y: "not", z: "touch"}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
descr string
|
|
||||||
expr string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{descr: "return as is", expr: `getOpaque`, wantErr: false},
|
|
||||||
{descr: "carry around ok", expr: `set x (getOpaque) ; $x`, wantErr: false},
|
|
||||||
{descr: "iterate over", expr: `foreach (countTo3) { |x| echo $x }`, wantErr: true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.descr, func(t *testing.T) {
|
|
||||||
|
|
||||||
outW := bytes.NewBuffer(nil)
|
|
||||||
inst := ucl.New(ucl.WithOut(outW))
|
|
||||||
|
|
||||||
inst.SetBuiltin("getOpaque", func(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
||||||
return ucl.Opaque(opaqueThing), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
res, err := inst.Eval(context.Background(), tt.expr)
|
|
||||||
if tt.wantErr {
|
|
||||||
assert.Error(t, err)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Same(t, opaqueThing, res)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("operate on opaques", func(t *testing.T) {
|
|
||||||
type opaqueThingType struct {
|
|
||||||
x string
|
|
||||||
y string
|
|
||||||
z string
|
|
||||||
}
|
|
||||||
opaqueThing := &opaqueThingType{x: "do", y: "not", z: "touch"}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
descr string
|
|
||||||
expr string
|
|
||||||
want opaqueThingType
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{descr: "return as is", expr: `getOpaque`, want: *opaqueThing},
|
|
||||||
{descr: "update pointer 1", expr: `set x (getOpaque) ; setProp $x -x "do" -y "touch" -z "this"`, want: opaqueThingType{x: "do", y: "touch", z: "this"}},
|
|
||||||
{descr: "update pointer 2", expr: `set x (getOpaque) ; setProp $x -x "yes" ; setProp $x -y "this" -z "too"`, want: opaqueThingType{x: "yes", y: "this", z: "too"}},
|
|
||||||
{descr: "bad args", expr: `set x (getOpaque) ; setProp $t -x "yes" ; setProp $bla -y "this" -z "too"`, want: *opaqueThing, wantErr: true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.descr, func(t *testing.T) {
|
|
||||||
|
|
||||||
outW := bytes.NewBuffer(nil)
|
|
||||||
inst := ucl.New(ucl.WithOut(outW))
|
|
||||||
|
|
||||||
inst.SetBuiltin("getOpaque", func(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
||||||
return ucl.Opaque(opaqueThing), nil
|
|
||||||
})
|
|
||||||
inst.SetBuiltin("setProp", func(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|
||||||
var o *opaqueThingType
|
|
||||||
|
|
||||||
if err := args.Bind(&o); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if o == nil {
|
|
||||||
return nil, errors.New("is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.HasSwitch("x") {
|
|
||||||
var s string
|
|
||||||
_ = args.BindSwitch("x", &s)
|
|
||||||
o.x = s
|
|
||||||
}
|
|
||||||
if args.HasSwitch("y") {
|
|
||||||
var s string
|
|
||||||
_ = args.BindSwitch("y", &s)
|
|
||||||
o.y = s
|
|
||||||
}
|
|
||||||
if args.HasSwitch("z") {
|
|
||||||
var s string
|
|
||||||
_ = args.BindSwitch("z", &s)
|
|
||||||
o.z = s
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err := inst.Eval(context.Background(), tt.expr)
|
|
||||||
if tt.wantErr {
|
|
||||||
assert.Error(t, err)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, tt.want, *opaqueThing)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCallArgs_Bind(t *testing.T) {
|
func TestCallArgs_Bind(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue