Compare commits

..

5 Commits

Author SHA1 Message Date
Leon Mika 9b3b8287fa Added strs:has-suffix
Build / build (push) Successful in 2m2s Details
2024-10-22 11:17:15 +11:00
Leon Mika c4e4a0977b Added strs:split
Build / build (push) Successful in 1m55s Details
This involved exposing the internal object types, which I think was about time
2024-10-22 09:14:24 +11:00
Leon Mika 2f54a9311e Finished implementing try catch
Build / build (push) Successful in 2m5s Details
2024-10-21 22:00:06 +11:00
lmika 5b913266e9 Merge pull request 'issue-2: added some more string functions' (#6) from feature/issue-2 into main
Build / build (push) Successful in 2m6s Details
Added the functions strs:to-upper, strs:to-lower, strs:trim
2024-10-21 10:01:48 +00:00
Leon Mika bb78a39cdb Started working on 'try' statement
Build / build (push) Successful in 2m8s Details
2024-10-21 15:21:28 +11:00
12 changed files with 550 additions and 133 deletions

View File

@ -68,6 +68,7 @@ type astDot struct {
}
type astCmd struct {
Pos lexer.Position
Name astDot `parser:"@@"`
Args []astDot `parser:"@@*"`
}

View File

@ -8,7 +8,7 @@ import (
"strings"
)
func echoBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func echoBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) == 0 {
if _, err := fmt.Fprintln(args.inst.Out()); err != nil {
return nil, err
@ -29,7 +29,7 @@ func echoBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return nil, nil
}
func addBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func addBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) == 0 {
return intObject(0), nil
}
@ -53,7 +53,7 @@ func addBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return intObject(n), nil
}
func subBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func subBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) == 0 {
return intObject(0), nil
}
@ -83,7 +83,7 @@ func subBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return intObject(n), nil
}
func mupBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func mupBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) == 0 {
return intObject(1), nil
}
@ -107,7 +107,7 @@ func mupBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return intObject(n), nil
}
func divBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func divBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) == 0 {
return intObject(1), nil
}
@ -137,7 +137,7 @@ func divBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return intObject(n), nil
}
func modBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func modBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) == 0 {
return intObject(0), nil
}
@ -167,7 +167,7 @@ func modBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return intObject(n), nil
}
func setBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func setBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -183,7 +183,7 @@ func setBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return newVal, nil
}
func toUpperBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func toUpperBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
@ -194,7 +194,7 @@ func toUpperBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return strObject(strings.ToUpper(sarg)), nil
}
func eqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func eqBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -205,7 +205,7 @@ func eqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return boolObject(objectsEqual(l, r)), nil
}
func neBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func neBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -216,7 +216,7 @@ func neBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return boolObject(!objectsEqual(l, r)), nil
}
func ltBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func ltBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -228,7 +228,7 @@ func ltBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return boolObject(isLess), nil
}
func leBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func leBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -240,7 +240,7 @@ func leBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return boolObject(isLess || objectsEqual(args.args[0], args.args[1])), nil
}
func gtBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func gtBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -252,7 +252,7 @@ func gtBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return boolObject(isGreater), nil
}
func geBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func geBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -264,7 +264,7 @@ func geBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return boolObject(isGreater || objectsEqual(args.args[0], args.args[1])), nil
}
func andBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func andBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -277,7 +277,7 @@ func andBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return args.args[len(args.args)-1], nil
}
func orBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func orBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -290,7 +290,7 @@ func orBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return boolObject(false), nil
}
func notBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func notBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
@ -300,7 +300,7 @@ func notBuiltin(ctx context.Context, args invocationArgs) (object, error) {
var errObjectsNotEqual = errors.New("objects not equal")
func objectsEqual(l, r object) bool {
func objectsEqual(l, r Object) bool {
if l == nil || r == nil {
return l == nil && r == nil
}
@ -318,8 +318,8 @@ func objectsEqual(l, r object) bool {
if rv, ok := r.(boolObject); ok {
return lv == rv
}
case listable:
rv, ok := r.(listable)
case Listable:
rv, ok := r.(Listable)
if !ok {
return false
}
@ -342,7 +342,7 @@ func objectsEqual(l, r object) bool {
if lv.Len() != rv.Len() {
return false
}
if err := lv.Each(func(k string, lkv object) error {
if err := lv.Each(func(k string, lkv Object) error {
rkv := rv.Value(k)
if rkv == nil {
return errObjectsNotEqual
@ -365,7 +365,7 @@ func objectsEqual(l, r object) bool {
return false
}
func objectsLessThan(l, r object) (bool, error) {
func objectsLessThan(l, r Object) (bool, error) {
switch lv := l.(type) {
case strObject:
if rv, ok := r.(strObject); ok {
@ -379,7 +379,7 @@ func objectsLessThan(l, r object) (bool, error) {
return false, errors.New("objects are not comparable")
}
func strBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func strBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
@ -391,7 +391,7 @@ func strBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return strObject(args.args[0].String()), nil
}
func intBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func intBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
@ -419,7 +419,7 @@ func intBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return nil, errors.New("cannot convert to int")
}
func concatBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func concatBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
var sb strings.Builder
for _, a := range args.args {
@ -432,7 +432,7 @@ func concatBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return strObject(sb.String()), nil
}
func callBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func callBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
@ -445,7 +445,7 @@ 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) {
func lenBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
@ -453,7 +453,7 @@ func lenBuiltin(ctx context.Context, args invocationArgs) (object, error) {
switch v := args.args[0].(type) {
case strObject:
return intObject(len(string(v))), nil
case listable:
case Listable:
return intObject(v.Len()), nil
case hashable:
return intObject(v.Len()), nil
@ -462,9 +462,9 @@ func lenBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return intObject(0), nil
}
func indexLookup(ctx context.Context, obj, elem object) (object, error) {
func indexLookup(ctx context.Context, obj, elem Object) (Object, error) {
switch v := obj.(type) {
case listable:
case Listable:
intIdx, ok := elem.(intObject)
if !ok {
return nil, nil
@ -483,7 +483,7 @@ func indexLookup(ctx context.Context, obj, elem object) (object, error) {
return nil, nil
}
func indexBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func indexBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
@ -500,7 +500,7 @@ func indexBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return val, nil
}
func keysBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func keysBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
@ -509,7 +509,7 @@ func keysBuiltin(ctx context.Context, args invocationArgs) (object, error) {
switch v := val.(type) {
case hashable:
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, strObject(k))
return nil
}); err != nil {
@ -521,7 +521,7 @@ func keysBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return nil, nil
}
func mapBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func mapBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -532,12 +532,12 @@ func mapBuiltin(ctx context.Context, args invocationArgs) (object, error) {
}
switch t := args.args[0].(type) {
case listable:
case Listable:
l := t.Len()
newList := listObject{}
for i := 0; i < l; i++ {
v := t.Index(i)
m, err := inv.invoke(ctx, args.fork([]object{v}))
m, err := inv.invoke(ctx, args.fork([]Object{v}))
if err != nil {
return nil, err
}
@ -548,7 +548,7 @@ func mapBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return nil, errors.New("expected listable")
}
func filterBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func filterBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
@ -559,12 +559,12 @@ func filterBuiltin(ctx context.Context, args invocationArgs) (object, error) {
}
switch t := args.args[0].(type) {
case listable:
case Listable:
l := t.Len()
newList := listObject{}
for i := 0; i < l; i++ {
v := t.Index(i)
m, err := inv.invoke(ctx, args.fork([]object{v}))
m, err := inv.invoke(ctx, args.fork([]Object{v}))
if err != nil {
return nil, err
} else if m.Truthy() {
@ -574,8 +574,8 @@ func filterBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return newList, nil
case hashable:
newHash := hashObject{}
if err := t.Each(func(k string, v object) error {
if m, err := inv.invoke(ctx, args.fork([]object{strObject(k), v})); err != nil {
if err := t.Each(func(k string, v Object) error {
if m, err := inv.invoke(ctx, args.fork([]Object{strObject(k), v})); err != nil {
return err
} else if m.Truthy() {
newHash[k] = v
@ -589,14 +589,14 @@ func filterBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return nil, errors.New("expected listable")
}
func reduceBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func reduceBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
var err error
if err = args.expectArgn(2); err != nil {
return nil, err
}
var (
accum object
accum Object
setFirst bool
block invokable
)
@ -615,7 +615,7 @@ func reduceBuiltin(ctx context.Context, args invocationArgs) (object, error) {
}
switch t := args.args[0].(type) {
case listable:
case Listable:
l := t.Len()
for i := 0; i < l; i++ {
v := t.Index(i)
@ -625,7 +625,7 @@ func reduceBuiltin(ctx context.Context, args invocationArgs) (object, error) {
continue
}
newAccum, err := block.invoke(ctx, args.fork([]object{v, accum}))
newAccum, err := block.invoke(ctx, args.fork([]Object{v, accum}))
if err != nil {
return nil, err
}
@ -635,8 +635,8 @@ func reduceBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return accum, nil
case hashable:
// TODO: should raise error?
if err := t.Each(func(k string, v object) error {
newAccum, err := block.invoke(ctx, args.fork([]object{strObject(k), v, accum}))
if err := t.Each(func(k string, v Object) error {
newAccum, err := block.invoke(ctx, args.fork([]Object{strObject(k), v, accum}))
if err != nil {
return err
}
@ -650,13 +650,13 @@ func reduceBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return nil, errors.New("expected listable")
}
func firstBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func firstBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
switch t := args.args[0].(type) {
case listable:
case Listable:
if t.Len() == 0 {
return nil, nil
}
@ -692,7 +692,7 @@ func (s seqObject) Len() int {
return l
}
func (s seqObject) Index(i int) object {
func (s seqObject) Index(i int) Object {
l := s.Len()
if i < 0 || i > l {
return nil
@ -703,7 +703,7 @@ func (s seqObject) Index(i int) object {
return intObject(s.from + i)
}
func seqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func seqBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
inclusive := false
if inc, ok := args.kwargs["inc"]; ok {
inclusive = (inc.Len() == 0) || inc.Truthy()
@ -732,7 +732,7 @@ func seqBuiltin(ctx context.Context, args invocationArgs) (object, error) {
}
}
func ifBuiltin(ctx context.Context, args macroArgs) (object, error) {
func ifBuiltin(ctx context.Context, args macroArgs) (Object, error) {
if args.nargs() < 2 {
return nil, errors.New("need at least 2 arguments")
}
@ -770,9 +770,64 @@ func ifBuiltin(ctx context.Context, args macroArgs) (object, error) {
return nil, errors.New("malformed if-elif-else")
}
func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
func tryBuiltin(ctx context.Context, args macroArgs) (_ Object, fnErr error) {
if args.nargs() < 1 {
return nil, errors.New("need at least 1 arguments")
}
var currError errObject
defer func() {
if errors.As(fnErr, &errBadUsage{}) {
return
}
if args.nargs() >= 2 && args.identIs(ctx, args.nargs()-2, "finally") {
var blockArgs []Object = nil
if fnErr != nil {
blockArgs = []Object{errObject{err: fnErr}}
}
_, err := args.evalBlock(ctx, args.nargs()-1, blockArgs, false)
if err != nil && fnErr == nil {
fnErr = err
}
}
}()
res, err := args.evalBlock(ctx, 0, nil, false)
args.shift(1)
if err == nil {
return res, nil
}
currError = errObject{err: err}
for args.identIs(ctx, 0, "catch") {
args.shift(1)
if args.nargs() < 1 {
return nil, errors.New("need at least 1 arguments")
}
res, err := args.evalBlock(ctx, 0, []Object{currError}, false)
if err == nil {
return res, nil
}
currError = errObject{err: err}
args.shift(1)
}
if args.identIs(ctx, 0, "finally") && args.nargs() > 2 {
return nil, tooManyFinallyBlocksError(args.ast.Pos)
}
return nil, currError.err
}
func foreachBuiltin(ctx context.Context, args macroArgs) (Object, error) {
var (
items object
items Object
blockIdx int
err error
)
@ -795,16 +850,16 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
}
var (
last object
last Object
breakErr errBreak
)
switch t := items.(type) {
case listable:
case Listable:
l := t.Len()
for i := 0; i < l; i++ {
v := t.Index(i)
last, err = args.evalBlock(ctx, blockIdx, []object{v}, true) // TO INCLUDE: the index
last, err = args.evalBlock(ctx, blockIdx, []Object{v}, true) // TO INCLUDE: the index
if err != nil {
if errors.As(err, &breakErr) {
if !breakErr.isCont {
@ -816,8 +871,8 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
}
}
case hashable:
err := t.Each(func(k string, v object) error {
last, err = args.evalBlock(ctx, blockIdx, []object{strObject(k), v}, true)
err := t.Each(func(k string, v Object) error {
last, err = args.evalBlock(ctx, blockIdx, []Object{strObject(k), v}, true)
return err
})
if errors.As(err, &breakErr) {
@ -832,25 +887,25 @@ func foreachBuiltin(ctx context.Context, args macroArgs) (object, error) {
return last, nil
}
func breakBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func breakBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) < 1 {
return nil, errBreak{}
}
return nil, errBreak{ret: args.args[0]}
}
func continueBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func continueBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
return nil, errBreak{isCont: true}
}
func returnBuiltin(ctx context.Context, args invocationArgs) (object, error) {
func returnBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) < 1 {
return nil, errReturn{}
}
return nil, errReturn{ret: args.args[0]}
}
func procBuiltin(ctx context.Context, args macroArgs) (object, error) {
func procBuiltin(ctx context.Context, args macroArgs) (Object, error) {
if args.nargs() < 1 {
return nil, errors.New("need at least one arguments")
}
@ -894,7 +949,7 @@ func (b procObject) Truthy() bool {
return true
}
func (b procObject) invoke(ctx context.Context, args invocationArgs) (object, error) {
func (b procObject) invoke(ctx context.Context, args invocationArgs) (Object, error) {
newEc := b.ec.fork()
for i, name := range b.block.Names {

View File

@ -13,6 +13,8 @@ func Strs() ucl.Module {
"to-upper": toUpper,
"to-lower": toLower,
"trim": trim,
"join": join,
"has-suffix": hasSuffix,
},
}
}
@ -43,3 +45,41 @@ func trim(ctx context.Context, args ucl.CallArgs) (any, error) {
return strings.TrimSpace(s), nil
}
func join(ctx context.Context, args ucl.CallArgs) (any, error) {
var (
l ucl.Listable
joinStr string
)
if err := args.Bind(&l, &joinStr); err != nil {
return nil, err
}
if l == nil {
return "", nil
} else if l.Len() == 0 {
return "", nil
}
sb := strings.Builder{}
for i := 0; i < l.Len(); i++ {
if i > 0 {
sb.WriteString(joinStr)
}
sb.WriteString(l.Index(i).String())
}
return sb.String(), nil
}
func hasSuffix(ctx context.Context, args ucl.CallArgs) (any, error) {
var (
s string
suffix string
)
if err := args.Bind(&s, &suffix); err != nil {
return nil, err
}
return strings.HasSuffix(s, suffix), nil
}

View File

@ -100,3 +100,66 @@ func TestStrs_Trim(t *testing.T) {
})
}
}
func TestStrs_HasSuffix(t *testing.T) {
tests := []struct {
desc string
eval string
want any
wantErr bool
}{
{desc: "suffix 1", eval: `strs:has-suffix "hello" "lo"`, want: true},
{desc: "suffix 2", eval: `strs:has-suffix "bellow" "low"`, want: true},
{desc: "suffix 3", eval: `strs:has-suffix "goodbye" "lo"`, want: false},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
inst := ucl.New(
ucl.WithModule(builtins.Strs()),
)
res, err := inst.Eval(context.Background(), tt.eval)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.want, res)
}
})
}
}
func TestStrs_Join(t *testing.T) {
tests := []struct {
desc string
eval string
want any
wantErr bool
}{
{desc: "join 1", eval: `strs:join ["a" "b" "c"] ","`, want: "a,b,c"},
{desc: "join 2", eval: `strs:join ["a" "b" "c"] "\n"`, want: "a\nb\nc"},
{desc: "join 3", eval: `strs:join ["a" "b" "c"] ""`, want: "abc"},
{desc: "join 4", eval: `strs:join [] ","`, want: ""},
// Hmm, not super happy about this one...
{desc: "join 5", eval: `strs:join ["a" "b"] ["what"]`, want: "a[what]b"},
{desc: "err join 1", eval: `strs:join 123 ","`, wantErr: true},
{desc: "err join 2", eval: `strs:join () ","`, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
inst := ucl.New(
ucl.WithModule(builtins.Strs()),
)
res, err := inst.Eval(context.Background(), tt.eval)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.want, res)
}
})
}
}

View File

@ -5,7 +5,7 @@ type evalCtx struct {
parent *evalCtx
commands map[string]invokable
macros map[string]macroable
vars map[string]object
vars map[string]Object
}
func (ec *evalCtx) forkAndIsolate() *evalCtx {
@ -34,7 +34,7 @@ func (ec *evalCtx) addMacro(name string, inv macroable) {
ec.root.macros[name] = inv
}
func (ec *evalCtx) setVar(name string, val object) bool {
func (ec *evalCtx) setVar(name string, val Object) bool {
if ec == nil || ec.vars == nil {
return false
}
@ -47,18 +47,18 @@ func (ec *evalCtx) setVar(name string, val object) bool {
return ec.parent.setVar(name, val)
}
func (ec *evalCtx) setOrDefineVar(name string, val object) {
func (ec *evalCtx) setOrDefineVar(name string, val Object) {
if ec.setVar(name, val) {
return
}
if ec.vars == nil {
ec.vars = make(map[string]object)
ec.vars = make(map[string]Object)
}
ec.vars[name] = val
}
func (ec *evalCtx) getVar(name string) (object, bool) {
func (ec *evalCtx) getVar(name string) (Object, bool) {
if ec.vars == nil {
return nil, false
}

37
ucl/errors.go Normal file
View File

@ -0,0 +1,37 @@
package ucl
import (
"fmt"
"github.com/alecthomas/participle/v2/lexer"
)
var (
tooManyFinallyBlocksError = newBadUsage("try needs at most 1 finally")
)
type errorWithPos struct {
err error
pos lexer.Position
}
func (e errorWithPos) Error() string {
return fmt.Sprintf("%v:%v - %v", e.pos.Line, e.pos.Offset, e.err.Error())
}
func (e errorWithPos) Unwrap() error {
return e.err
}
type errBadUsage struct {
msg string
}
func newBadUsage(msg string) func(pos lexer.Position) error {
return func(pos lexer.Position) error {
return errorWithPos{err: errBadUsage{msg: msg}, pos: pos}
}
}
func (e errBadUsage) Error() string {
return "bad usage: " + e.msg
}

View File

@ -10,7 +10,7 @@ type evaluator struct {
inst *Inst
}
func (e evaluator) evalBlock(ctx context.Context, ec *evalCtx, n *astBlock) (lastRes object, err error) {
func (e evaluator) evalBlock(ctx context.Context, ec *evalCtx, n *astBlock) (lastRes Object, err error) {
// TODO: push scope?
for _, s := range n.Statements {
@ -22,11 +22,11 @@ func (e evaluator) evalBlock(ctx context.Context, ec *evalCtx, n *astBlock) (las
return lastRes, nil
}
func (e evaluator) evalScript(ctx context.Context, ec *evalCtx, n *astScript) (lastRes object, err error) {
func (e evaluator) evalScript(ctx context.Context, ec *evalCtx, n *astScript) (lastRes Object, err error) {
return e.evalStatement(ctx, ec, n.Statements)
}
func (e evaluator) evalStatement(ctx context.Context, ec *evalCtx, n *astStatements) (object, error) {
func (e evaluator) evalStatement(ctx context.Context, ec *evalCtx, n *astStatements) (Object, error) {
if n == nil {
return nil, nil
}
@ -49,7 +49,7 @@ func (e evaluator) evalStatement(ctx context.Context, ec *evalCtx, n *astStateme
return res, nil
}
func (e evaluator) evalPipeline(ctx context.Context, ec *evalCtx, n *astPipeline) (object, error) {
func (e evaluator) evalPipeline(ctx context.Context, ec *evalCtx, n *astPipeline) (Object, error) {
res, err := e.evalCmd(ctx, ec, nil, n.First)
if err != nil {
return nil, err
@ -69,7 +69,7 @@ func (e evaluator) evalPipeline(ctx context.Context, ec *evalCtx, n *astPipeline
return res, nil
}
func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentPipe object, ast *astCmd) (object, error) {
func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentPipe Object, ast *astCmd) (Object, error) {
switch {
case (ast.Name.Arg.Ident != nil) && len(ast.Name.DotSuffix) == 0:
name := ast.Name.Arg.Ident.String()
@ -105,7 +105,7 @@ func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentPipe object,
return nameElem, nil
}
func (e evaluator) evalInvokable(ctx context.Context, ec *evalCtx, currentPipe object, ast *astCmd, cmd invokable) (object, error) {
func (e evaluator) evalInvokable(ctx context.Context, ec *evalCtx, currentPipe Object, ast *astCmd, cmd invokable) (Object, error) {
var (
pargs listObject
kwargs map[string]*listObject
@ -138,7 +138,7 @@ func (e evaluator) evalInvokable(ctx context.Context, ec *evalCtx, currentPipe o
return cmd.invoke(ctx, invArgs)
}
func (e evaluator) evalMacro(ctx context.Context, ec *evalCtx, hasPipe bool, pipeArg object, ast *astCmd, cmd macroable) (object, error) {
func (e evaluator) evalMacro(ctx context.Context, ec *evalCtx, hasPipe bool, pipeArg Object, ast *astCmd, cmd macroable) (Object, error) {
return cmd.invokeMacro(ctx, macroArgs{
eval: e,
ec: ec,
@ -148,7 +148,7 @@ func (e evaluator) evalMacro(ctx context.Context, ec *evalCtx, hasPipe bool, pip
})
}
func (e evaluator) evalDot(ctx context.Context, ec *evalCtx, n astDot) (object, error) {
func (e evaluator) evalDot(ctx context.Context, ec *evalCtx, n astDot) (Object, error) {
res, err := e.evalArg(ctx, ec, n.Arg)
if err != nil {
return nil, err
@ -157,7 +157,7 @@ func (e evaluator) evalDot(ctx context.Context, ec *evalCtx, n astDot) (object,
}
for _, dot := range n.DotSuffix {
var idx object
var idx Object
if dot.KeyIdent != nil {
idx = strObject(dot.KeyIdent.String())
} else {
@ -175,7 +175,7 @@ func (e evaluator) evalDot(ctx context.Context, ec *evalCtx, n astDot) (object,
return res, nil
}
func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (object, error) {
func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (Object, error) {
switch {
case n.Literal != nil:
return e.evalLiteral(ctx, ec, n.Literal)
@ -200,7 +200,7 @@ func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (objec
return nil, errors.New("unhandled arg type")
}
func (e evaluator) evalListOrHash(ctx context.Context, ec *evalCtx, loh *astListOrHash) (object, error) {
func (e evaluator) evalListOrHash(ctx context.Context, ec *evalCtx, loh *astListOrHash) (Object, error) {
if loh.EmptyList {
return listObject{}, nil
} else if loh.EmptyHash {
@ -243,7 +243,7 @@ func (e evaluator) evalListOrHash(ctx context.Context, ec *evalCtx, loh *astList
return l, nil
}
func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n *astLiteral) (object, error) {
func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n *astLiteral) (Object, error) {
switch {
case n.Str != nil:
uq, err := strconv.Unquote(*n.Str)
@ -257,7 +257,7 @@ func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n *astLiteral)
return nil, errors.New("unhandled literal type")
}
func (e evaluator) evalSub(ctx context.Context, ec *evalCtx, n *astPipeline) (object, error) {
func (e evaluator) evalSub(ctx context.Context, ec *evalCtx, n *astPipeline) (Object, error) {
pipelineRes, err := e.evalPipeline(ctx, ec, n)
if err != nil {
return nil, err

View File

@ -14,13 +14,13 @@ func EvalAndDisplay(ctx context.Context, inst *Inst, expr string) error {
return displayResult(ctx, inst, res)
}
func displayResult(ctx context.Context, inst *Inst, res object) (err error) {
func displayResult(ctx context.Context, inst *Inst, res Object) (err error) {
switch v := res.(type) {
case nil:
if _, err = fmt.Fprintln(inst.out, "(nil)"); err != nil {
return err
}
case listable:
case Listable:
for i := 0; i < v.Len(); i++ {
if err = displayResult(ctx, inst, v.Index(i)); err != nil {
return err

View File

@ -88,6 +88,7 @@ func New(opts ...InstOption) *Inst {
rootEC.addMacro("if", macroFunc(ifBuiltin))
rootEC.addMacro("foreach", macroFunc(foreachBuiltin))
rootEC.addMacro("proc", macroFunc(procBuiltin))
rootEC.addMacro("try", macroFunc(tryBuiltin))
inst := &Inst{
out: os.Stdout,
@ -133,7 +134,7 @@ func (inst *Inst) Eval(ctx context.Context, expr string) (any, error) {
return goRes, nil
}
func (inst *Inst) eval(ctx context.Context, expr string) (object, error) {
func (inst *Inst) eval(ctx context.Context, expr string) (Object, error) {
ast, err := parse(strings.NewReader(expr))
if err != nil {
return nil, err

View File

@ -11,30 +11,30 @@ import (
"github.com/lmika/gopkgs/fp/slices"
)
type object interface {
type Object interface {
String() string
Truthy() bool
}
type listable interface {
type Listable interface {
Len() int
Index(i int) object
Index(i int) Object
}
type hashable interface {
Len() int
Value(k string) object
Each(func(k string, v object) error) error
Value(k string) Object
Each(func(k string, v Object) error) error
}
type listObject []object
type listObject []Object
func (lo *listObject) Append(o object) {
func (lo *listObject) Append(o Object) {
*lo = append(*lo, o)
}
func (s listObject) String() string {
return fmt.Sprintf("%v", []object(s))
return fmt.Sprintf("%v", []Object(s))
}
func (s listObject) Truthy() bool {
@ -45,11 +45,11 @@ func (s listObject) Len() int {
return len(s)
}
func (s listObject) Index(i int) object {
func (s listObject) Index(i int) Object {
return s[i]
}
type hashObject map[string]object
type hashObject map[string]Object
func (s hashObject) String() string {
if len(s) == 0 {
@ -78,11 +78,11 @@ func (s hashObject) Len() int {
return len(s)
}
func (s hashObject) Value(k string) object {
func (s hashObject) Value(k string) Object {
return s[k]
}
func (s hashObject) Each(fn func(k string, v object) error) error {
func (s hashObject) Each(fn func(k string, v Object) error) error {
for k, v := range s {
if err := fn(k, v); err != nil {
return err
@ -124,7 +124,7 @@ func (b boolObject) Truthy() bool {
return bool(b)
}
func toGoValue(obj object) (interface{}, bool) {
func toGoValue(obj Object) (interface{}, bool) {
switch v := obj.(type) {
case OpaqueObject:
return v.v, true
@ -167,7 +167,7 @@ func toGoValue(obj object) (interface{}, bool) {
return nil, false
}
func fromGoValue(v any) (object, error) {
func fromGoValue(v any) (Object, error) {
switch t := v.(type) {
case OpaqueObject:
return t, nil
@ -184,7 +184,7 @@ func fromGoValue(v any) (object, error) {
return fromGoReflectValue(reflect.ValueOf(v))
}
func fromGoReflectValue(resVal reflect.Value) (object, error) {
func fromGoReflectValue(resVal reflect.Value) (Object, error) {
if !resVal.IsValid() {
return nil, nil
}
@ -212,7 +212,7 @@ type macroArgs struct {
eval evaluator
ec *evalCtx
hasPipe bool
pipeArg object
pipeArg Object
ast *astCmd
argShift int
}
@ -259,7 +259,7 @@ func (ma *macroArgs) shiftIdent(ctx context.Context) (string, bool) {
return "", false
}
func (ma macroArgs) evalArg(ctx context.Context, n int) (object, error) {
func (ma macroArgs) evalArg(ctx context.Context, n int) (Object, error) {
if n >= len(ma.ast.Args[ma.argShift:]) {
return nil, errors.New("not enough arguments") // FIX
}
@ -267,7 +267,7 @@ func (ma macroArgs) evalArg(ctx context.Context, n int) (object, error) {
return ma.eval.evalDot(ctx, ma.ec, ma.ast.Args[ma.argShift+n])
}
func (ma macroArgs) evalBlock(ctx context.Context, n int, args []object, pushScope bool) (object, error) {
func (ma macroArgs) evalBlock(ctx context.Context, n int, args []Object, pushScope bool) (Object, error) {
obj, err := ma.evalArg(ctx, n)
if err != nil {
return nil, err
@ -303,11 +303,21 @@ func (ma macroArgs) evalBlock(ctx context.Context, n int, args []object, pushSco
return nil, errors.New("expected an invokable arg")
}
type errObject struct{ err error }
func (eo errObject) String() string {
return "error:" + eo.err.Error()
}
func (eo errObject) Truthy() bool {
return true
}
type invocationArgs struct {
eval evaluator
inst *Inst
ec *evalCtx
args []object
args []Object
kwargs map[string]*listObject
}
@ -360,7 +370,7 @@ func (ia invocationArgs) invokableArg(i int) (invokable, error) {
return nil, errors.New("expected an invokable arg")
}
func (ia invocationArgs) fork(args []object) invocationArgs {
func (ia invocationArgs) fork(args []Object) invocationArgs {
return invocationArgs{
eval: ia.eval,
inst: ia.inst,
@ -383,22 +393,22 @@ func (ia invocationArgs) shift(i int) invocationArgs {
}
}
// invokable is an object that can be executed as a command
// invokable is an Object that can be executed as a command
type invokable interface {
invoke(ctx context.Context, args invocationArgs) (object, error)
invoke(ctx context.Context, args invocationArgs) (Object, error)
}
type macroable interface {
invokeMacro(ctx context.Context, args macroArgs) (object, error)
invokeMacro(ctx context.Context, args macroArgs) (Object, error)
}
type pipeInvokable interface {
invokable
}
type invokableFunc func(ctx context.Context, args invocationArgs) (object, error)
type invokableFunc func(ctx context.Context, args invocationArgs) (Object, error)
func (i invokableFunc) invoke(ctx context.Context, args invocationArgs) (object, error) {
func (i invokableFunc) invoke(ctx context.Context, args invocationArgs) (Object, error) {
return i(ctx, args)
}
@ -414,7 +424,7 @@ func (bo blockObject) Truthy() bool {
return len(bo.block.Statements) > 0
}
func (bo blockObject) invoke(ctx context.Context, args invocationArgs) (object, error) {
func (bo blockObject) invoke(ctx context.Context, args invocationArgs) (Object, error) {
ec := args.ec.fork()
for i, n := range bo.block.Names {
if i < len(args.args) {
@ -425,13 +435,13 @@ func (bo blockObject) invoke(ctx context.Context, args invocationArgs) (object,
return args.eval.evalBlock(ctx, ec, bo.block)
}
type macroFunc func(ctx context.Context, args macroArgs) (object, error)
type macroFunc func(ctx context.Context, args macroArgs) (Object, error)
func (i macroFunc) invokeMacro(ctx context.Context, args macroArgs) (object, error) {
func (i macroFunc) invokeMacro(ctx context.Context, args macroArgs) (Object, error) {
return i(ctx, args)
}
func isTruthy(obj object) bool {
func isTruthy(obj Object) bool {
if obj == nil {
return false
}
@ -467,7 +477,7 @@ func (p listableProxyObject) Len() int {
return p.v.Len()
}
func (p listableProxyObject) Index(i int) object {
func (p listableProxyObject) Index(i int) Object {
e, err := fromGoValue(p.v.Index(i).Interface())
if err != nil {
return nil
@ -501,7 +511,7 @@ func (s structProxyObject) Len() int {
return len(s.vf)
}
func (s structProxyObject) Value(k string) object {
func (s structProxyObject) Value(k string) Object {
f := s.v.FieldByName(k)
if !f.IsValid() {
return nil
@ -521,7 +531,7 @@ func (s structProxyObject) Value(k string) object {
return e
}
func (s structProxyObject) Each(fn func(k string, v object) error) error {
func (s structProxyObject) Each(fn func(k string, v Object) error) error {
for _, f := range s.vf {
v, err := fromGoValue(s.v.FieldByName(f.Name).Interface())
if err != nil {
@ -553,7 +563,7 @@ func (p OpaqueObject) Truthy() bool {
type errBreak struct {
isCont bool
ret object
ret Object
}
func (e errBreak) Error() string {
@ -564,7 +574,7 @@ func (e errBreak) Error() string {
}
type errReturn struct {
ret object
ret Object
}
func (e errReturn) Error() string {

View File

@ -3,6 +3,7 @@ package ucl
import (
"bytes"
"context"
"errors"
"fmt"
"strings"
"testing"
@ -13,15 +14,15 @@ import (
// Builtins used for test
func WithTestBuiltin() InstOption {
return func(i *Inst) {
i.rootEC.addCmd("firstarg", invokableFunc(func(ctx context.Context, args invocationArgs) (object, error) {
i.rootEC.addCmd("firstarg", invokableFunc(func(ctx context.Context, args invocationArgs) (Object, error) {
return args.args[0], nil
}))
i.rootEC.addCmd("toUpper", invokableFunc(func(ctx context.Context, args invocationArgs) (object, error) {
i.rootEC.addCmd("toUpper", invokableFunc(func(ctx context.Context, args invocationArgs) (Object, error) {
return strObject(strings.ToUpper(args.args[0].String())), nil
}))
i.rootEC.addCmd("sjoin", invokableFunc(func(ctx context.Context, args invocationArgs) (object, error) {
i.rootEC.addCmd("sjoin", invokableFunc(func(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) == 0 {
return strObject(""), nil
}
@ -36,14 +37,21 @@ func WithTestBuiltin() InstOption {
return strObject(line.String()), nil
}))
i.rootEC.addCmd("list", invokableFunc(func(ctx context.Context, args invocationArgs) (object, error) {
i.rootEC.addCmd("list", invokableFunc(func(ctx context.Context, args invocationArgs) (Object, error) {
return listObject(args.args), nil
}))
i.rootEC.addCmd("joinpipe", invokableFunc(func(ctx context.Context, args invocationArgs) (object, error) {
i.rootEC.addCmd("error", invokableFunc(func(ctx context.Context, args invocationArgs) (Object, error) {
if len(args.args) == 0 {
return nil, errors.New("an error occurred")
}
return nil, errors.New(args.args[0].String())
}))
i.rootEC.addCmd("joinpipe", invokableFunc(func(ctx context.Context, args invocationArgs) (Object, error) {
sb := strings.Builder{}
lst, ok := args.args[0].(listable)
lst, ok := args.args[0].(Listable)
if !ok {
return strObject(""), nil
}
@ -192,6 +200,201 @@ func TestBuiltins_If(t *testing.T) {
}
}
func TestBuiltins_Try(t *testing.T) {
tests := []struct {
desc string
expr string
want string
wantErr string
}{
{desc: "single try - successful", expr: `
try {
echo "good"
}
echo "after"`, want: "good\nafter\n(nil)\n"},
{desc: "single try - unsuccessful", expr: `
try {
error "bang"
}
echo "after"`, wantErr: "bang"},
{desc: "try with catch - successful", expr: `
try {
echo "good"
} catch {
echo "something happened"
}
echo "after"`, want: "good\nafter\n(nil)\n"},
{desc: "try with catch - unsuccessful", expr: `
try {
error "bang"
} catch {
echo "something happened"
}
echo "after"`, want: "something happened\nafter\n(nil)\n"},
{desc: "try with catch with passed in error - unsuccessful", expr: `
try {
error "bang"
} catch { |err|
echo (cat "the error was = " $err)
}
echo "after"`, want: "the error was = error:bang\nafter\n(nil)\n"},
{desc: "try with two catch - successful", expr: `
try {
echo "i'm good"
} catch {
echo "i'm also good"
} catch {
echo "wow, we made it here"
}
echo "after"`, want: "i'm good\nafter\n(nil)\n"},
{desc: "try with two catch - first unsuccessful", expr: `
try {
error "bang"
} catch {
echo "i'm also good"
} catch {
echo "wow, we made it here"
}
echo "after"`, want: "i'm also good\nafter\n(nil)\n"},
{desc: "try with two catch - both unsuccessful", expr: `
try {
error "bang"
} catch {
error "boom"
} catch {
echo "wow, we made it here"
}
echo "after"`, want: "wow, we made it here\nafter\n(nil)\n"},
{desc: "return value - single try", expr: `
set x (try { error "bang" })
$x`, wantErr: "bang"},
{desc: "return value - single try", expr: `
set x (try { error "bang" } catch { |err| $err })
$x`, want: "error:bang\n"},
{desc: "return value - try and catch - successful", expr: `
set x (try { error "bang" } catch { "hello" })
$x`, want: "hello\n"},
{desc: "return value - try and catch - unsuccessful", expr: `
set x (try { error "bang" } catch { error "boom" })
$x`, wantErr: "boom"},
{desc: "try with finally - successful", expr: `
try {
echo "all good"
} finally {
echo "always at end"
}
echo "after"`, want: "all good\nalways at end\nafter\n(nil)\n"},
{desc: "try with finally - unsuccessful", expr: `
try {
error "bang"
} finally {
echo "always at end"
}
echo "after"`, want: "always at end\n", wantErr: "bang"},
{desc: "try with catch and finally - successful", expr: `
try {
echo "all good"
} catch {
echo "was caught"
} finally {
echo "always at end"
}
echo "after"`, want: "all good\nalways at end\nafter\n(nil)\n"},
{desc: "try with catch and finally - unsuccessful", expr: `
try {
error "bang"
} catch {
echo "was caught"
} finally {
echo "always at end"
}
echo "after"`, want: "was caught\nalways at end\nafter\n(nil)\n"},
{desc: "try with catch and finally - catch unsuccessful", expr: `
try {
error "bang"
} catch {
error "boom"
} finally {
echo "always at end"
}
echo "after"`, want: "always at end\n", wantErr: "boom"},
{desc: "try with finally - finally result discarded", expr: `
set a (try {
"return me"
} finally {
"not met"
})
echo $a`, want: "return me\n(nil)\n"},
{desc: "try with finally - error discarded if try fails result discarded", expr: `
try {
error "bang"
} finally {
error "kaboom"
}`, wantErr: "bang"},
{desc: "try with finally - error not discarded if try succeeds", expr: `
try {
echo "all good"
} finally {
error "kaboom"
}`, want: "all good\n", wantErr: "kaboom"},
{desc: "try with finally with error - successful", expr: `
try {
echo "all good"
} finally { |err|
echo (cat "the error was " $err)
if (eq $err ()) { echo "that's nil" }
}
echo "after"`, want: "all good\nthe error was \nthat's nil\nafter\n(nil)\n"},
{desc: "try with finally - unsuccessful", expr: `
try {
error "bang"
} finally { |err|
echo (cat "the error was " $err)
}
echo "after"`, want: "the error was error:bang\n", wantErr: "bang"},
{desc: "try with too many finallies - unsuccessful", expr: `
try {
error "bang"
} finally {
echo "and do this"
} finally { |err|
echo (cat "the error was " $err)
}
echo "after"`, wantErr: "2:4 - bad usage: try needs at most 1 finally"},
{desc: "try with finally in catch - unsuccessful", expr: `
try {
try {
error "bang"
} finally { |err|
echo (cat "the error was " $err)
}
} catch {
echo "outer caught"
} finally {
echo "outer"
}
echo "after"`, want: "the error was error:bang\nouter caught\nouter\nafter\n(nil)\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())
err := EvalAndDisplay(ctx, inst, tt.expr)
if tt.wantErr != "" {
assert.Error(t, err)
assert.Equal(t, tt.wantErr, err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, outW.String())
})
}
}
func TestBuiltins_ForEach(t *testing.T) {
tests := []struct {
desc string

View File

@ -85,7 +85,7 @@ type userBuiltin struct {
fn func(ctx context.Context, args CallArgs) (any, error)
}
func (u userBuiltin) invoke(ctx context.Context, args invocationArgs) (object, error) {
func (u userBuiltin) invoke(ctx context.Context, args invocationArgs) (Object, error) {
v, err := u.fn(ctx, CallArgs{args: args})
if err != nil {
return nil, err
@ -94,7 +94,7 @@ func (u userBuiltin) invoke(ctx context.Context, args invocationArgs) (object, e
return fromGoValue(v)
}
func (ca CallArgs) bindArg(v interface{}, arg object) error {
func (ca CallArgs) bindArg(v interface{}, arg Object) error {
switch t := v.(type) {
case *interface{}:
*t, _ = toGoValue(arg)
@ -110,6 +110,13 @@ func (ca CallArgs) bindArg(v interface{}, arg object) error {
ec: ca.args.ec,
}
return nil
case *Listable:
i, ok := arg.(Listable)
if !ok {
return errors.New("exepected listable")
}
*t = i
return nil
case *string:
if arg != nil {
*t = arg.String()
@ -153,7 +160,7 @@ func (ca CallArgs) bindArg(v interface{}, arg object) error {
return nil
}
func canBindArg(v interface{}, arg object) bool {
func canBindArg(v interface{}, arg Object) bool {
switch v.(type) {
case *string:
return true
@ -233,7 +240,7 @@ type missingHandlerInvokable struct {
handler MissingBuiltinHandler
}
func (m missingHandlerInvokable) invoke(ctx context.Context, args invocationArgs) (object, error) {
func (m missingHandlerInvokable) invoke(ctx context.Context, args invocationArgs) (Object, error) {
v, err := m.handler(ctx, m.name, CallArgs{args: args})
if err != nil {
return nil, err
@ -265,7 +272,7 @@ func (i Invokable) Invoke(ctx context.Context, args ...any) (any, error) {
inst: i.inst,
}
invArgs.args, err = slices.MapWithError(args, func(a any) (object, error) {
invArgs.args, err = slices.MapWithError(args, func(a any) (Object, error) {
return fromGoValue(a)
})
if err != nil {