Added some changes to call and added builtins
All checks were successful
Build / build (push) Successful in 2m32s
All checks were successful
Build / build (push) Successful in 2m32s
- call now supports calling invokables by string - call now takes as arguments a listable of positional args, and a hashable of keyword args - added strs:split, strs:join, and strs:has-prefix - added new lists module with lists:first - added time:sleep
This commit is contained in:
parent
109be33d14
commit
e7f904e7da
|
@ -24,6 +24,7 @@ func main() {
|
|||
ucl.WithModule(builtins.Itrs()),
|
||||
ucl.WithModule(builtins.OS()),
|
||||
ucl.WithModule(builtins.Strs()),
|
||||
ucl.WithModule(builtins.Lists()),
|
||||
ucl.WithModule(builtins.Time()),
|
||||
)
|
||||
ctx := context.Background()
|
||||
|
|
|
@ -27,6 +27,7 @@ func initJS(ctx context.Context) {
|
|||
ucl.WithModule(builtins.Strs()),
|
||||
ucl.WithModule(builtins.Time()),
|
||||
ucl.WithModule(builtins.Itrs()),
|
||||
ucl.WithModule(builtins.Lists()),
|
||||
ucl.WithOut(ucl.LineHandler(func(line string) {
|
||||
invokeUCLCallback("onOutLine", line)
|
||||
})),
|
||||
|
|
|
@ -451,12 +451,58 @@ func callBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
inv, ok := args.args[0].(invokable)
|
||||
if !ok {
|
||||
return nil, errors.New("expected invokable")
|
||||
var inv invokable
|
||||
switch t := args.args[0].(type) {
|
||||
case invokable:
|
||||
inv = t
|
||||
case StringObject:
|
||||
inv = args.ec.lookupInvokable(t.String())
|
||||
if inv == nil {
|
||||
return nil, errors.New("no such invokable: " + t.String())
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("expected string or invokable")
|
||||
}
|
||||
|
||||
return inv.invoke(ctx, args.shift(1))
|
||||
var calledArgs []Object
|
||||
|
||||
args = args.shift(1)
|
||||
if len(args.args) > 0 {
|
||||
argList, ok := args.args[0].(Listable)
|
||||
if !ok {
|
||||
return nil, errors.New("expected listable arg")
|
||||
}
|
||||
calledArgs = make([]Object, argList.Len())
|
||||
for i := 0; i < argList.Len(); i++ {
|
||||
calledArgs[i] = argList.Index(i)
|
||||
}
|
||||
args.shift(1)
|
||||
}
|
||||
|
||||
invArgs := args.fork(calledArgs)
|
||||
|
||||
args = args.shift(1)
|
||||
if len(args.args) > 0 {
|
||||
kwArgs, ok := args.args[0].(Hashable)
|
||||
if !ok {
|
||||
return nil, errors.New("expected hashable arg")
|
||||
}
|
||||
kwArgs.Each(func(k string, v Object) error {
|
||||
if invArgs.kwargs == nil {
|
||||
invArgs.kwargs = make(map[string]*ListObject)
|
||||
}
|
||||
if invArgs.kwargs[k] == nil {
|
||||
invArgs.kwargs[k] = &ListObject{}
|
||||
}
|
||||
|
||||
kwArg := *(invArgs.kwargs[k])
|
||||
kwArg = append(kwArg, v)
|
||||
invArgs.kwargs[k] = &kwArg
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return inv.invoke(ctx, invArgs)
|
||||
}
|
||||
|
||||
func lenBuiltin(ctx context.Context, args invocationArgs) (Object, error) {
|
||||
|
@ -495,6 +541,8 @@ func indexLookup(ctx context.Context, obj, elem Object) (Object, error) {
|
|||
}
|
||||
if int(intIdx) >= 0 && int(intIdx) < v.Len() {
|
||||
return v.Index(int(intIdx)), nil
|
||||
} else if int(intIdx) < 0 && int(intIdx) >= -v.Len() {
|
||||
return v.Index(v.Len() + int(intIdx)), nil
|
||||
}
|
||||
return nil, nil
|
||||
case Hashable:
|
||||
|
|
60
ucl/builtins/lists.go
Normal file
60
ucl/builtins/lists.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package builtins
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
func Lists() ucl.Module {
|
||||
return ucl.Module{
|
||||
Name: "lists",
|
||||
Builtins: map[string]ucl.BuiltinHandler{
|
||||
"first": listFirst,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func listFirst(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var (
|
||||
what ucl.Object
|
||||
count int
|
||||
)
|
||||
|
||||
if err := args.Bind(&what, &count); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return ucl.NewListObject(), nil
|
||||
}
|
||||
|
||||
newList := ucl.NewListObject()
|
||||
|
||||
switch t := what.(type) {
|
||||
case ucl.Listable:
|
||||
if count < 0 {
|
||||
count = t.Len() + count
|
||||
}
|
||||
|
||||
for i := 0; i < min(count, t.Len()); i++ {
|
||||
newList.Append(t.Index(i))
|
||||
}
|
||||
case ucl.Iterable:
|
||||
if count < 0 {
|
||||
return nil, errors.New("negative counts not supported on iters")
|
||||
}
|
||||
|
||||
for i := 0; t.HasNext() && i < count; i++ {
|
||||
v, err := t.Next(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newList.Append(v)
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("expected listable")
|
||||
}
|
||||
|
||||
return newList, nil
|
||||
}
|
59
ucl/builtins/lists_test.go
Normal file
59
ucl/builtins/lists_test.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package builtins_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"ucl.lmika.dev/ucl"
|
||||
"ucl.lmika.dev/ucl/builtins"
|
||||
)
|
||||
|
||||
func TestLists_First(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
eval string
|
||||
want any
|
||||
wantErr bool
|
||||
}{
|
||||
{desc: "firsts 1", eval: `lists:first [1 2 3 4 5] 0`, want: []any{}},
|
||||
{desc: "firsts 2", eval: `lists:first [1 2 3 4 5] 3`, want: []any{1, 2, 3}},
|
||||
{desc: "firsts 3", eval: `lists:first [1 2 3] 5`, want: []any{1, 2, 3}},
|
||||
{desc: "firsts 4", eval: `lists:first [1 2 3] 1`, want: []any{1}},
|
||||
{desc: "firsts 5", eval: `lists:first [1 2 3 4 5] -1`, want: []any{1, 2, 3, 4}},
|
||||
{desc: "firsts 6", eval: `lists:first [1 2 3 4 5] -3`, want: []any{1, 2}},
|
||||
{desc: "firsts 7", eval: `lists:first [1 2 3 4 5] -8`, want: []any{}},
|
||||
{desc: "firsts 8", eval: `lists:first (itrs:from [1 2 3 4 5]) 3`, want: []any{1, 2, 3}},
|
||||
{desc: "firsts 9", eval: `lists:first (itrs:from [1 2 3]) 5`, want: []any{1, 2, 3}},
|
||||
|
||||
{desc: "err 1", eval: `lists:first (itrs:from [1 2 3]) -3`, wantErr: true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
inst := ucl.New(
|
||||
ucl.WithModule(builtins.Itrs()),
|
||||
ucl.WithModule(builtins.Lists()),
|
||||
)
|
||||
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 uclListOf(args ...any) *ucl.ListObject {
|
||||
newList := ucl.NewListObject()
|
||||
for _, arg := range args {
|
||||
switch t := arg.(type) {
|
||||
case int:
|
||||
newList.Append(ucl.IntObject(t))
|
||||
default:
|
||||
panic("unhandled type")
|
||||
}
|
||||
}
|
||||
return newList
|
||||
}
|
|
@ -2,6 +2,7 @@ package builtins
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
@ -10,9 +11,12 @@ func Strs() ucl.Module {
|
|||
return ucl.Module{
|
||||
Name: "strs",
|
||||
Builtins: map[string]ucl.BuiltinHandler{
|
||||
"to-upper": toUpper,
|
||||
"to-lower": toLower,
|
||||
"trim": trim,
|
||||
"to-upper": toUpper,
|
||||
"to-lower": toLower,
|
||||
"trim": trim,
|
||||
"split": split,
|
||||
"join": join,
|
||||
"has-prefix": hasPrefix,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -43,3 +47,86 @@ func trim(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|||
|
||||
return strings.TrimSpace(s), nil
|
||||
}
|
||||
|
||||
func hasPrefix(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var s, prefix string
|
||||
if err := args.Bind(&s, &prefix); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return strings.HasPrefix(s, prefix), nil
|
||||
}
|
||||
|
||||
func split(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var s string
|
||||
if err := args.Bind(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sep := ""
|
||||
if args.NArgs() > 0 {
|
||||
if err := args.Bind(&sep); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
n := -1
|
||||
if args.HasSwitch("max") {
|
||||
if err := args.BindSwitch("max", &n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return StringSlice(strings.SplitN(s, sep, n)), nil
|
||||
}
|
||||
|
||||
func join(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var (
|
||||
what ucl.Object
|
||||
tok string
|
||||
)
|
||||
|
||||
if err := args.Bind(&what); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if args.NArgs() > 0 {
|
||||
if err := args.Bind(&tok); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
switch t := what.(type) {
|
||||
case ucl.Listable:
|
||||
var sb strings.Builder
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
if i > 0 {
|
||||
sb.WriteString(tok)
|
||||
}
|
||||
sb.WriteString(t.Index(i).String())
|
||||
}
|
||||
return sb.String(), nil
|
||||
case ucl.Iterable:
|
||||
first := true
|
||||
|
||||
var sb strings.Builder
|
||||
for t.HasNext() {
|
||||
v, err := t.Next(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !first {
|
||||
sb.WriteString(tok)
|
||||
} else {
|
||||
first = false
|
||||
}
|
||||
sb.WriteString(v.String())
|
||||
}
|
||||
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
return nil, errors.New("expected listable or iterable as arg 1")
|
||||
}
|
||||
|
||||
type StringSlice []string
|
||||
|
|
|
@ -100,3 +100,104 @@ func TestStrs_Trim(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrs_HasPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
eval string
|
||||
want any
|
||||
wantErr bool
|
||||
}{
|
||||
{desc: "has prefix 1", eval: `strs:has-prefix "hello, world" "hello"`, want: true},
|
||||
{desc: "has prefix 2", eval: `strs:has-prefix "goodbye, world" "hello"`, want: false},
|
||||
{desc: "has prefix 3", eval: `strs:has-prefix "" "world"`, want: false},
|
||||
{desc: "has prefix 4", eval: `strs:has-prefix "hello" ""`, want: true},
|
||||
|
||||
{desc: "err 1", eval: `strs:has-prefix`, wantErr: true},
|
||||
{desc: "err 1", eval: `strs:has-prefix "asd"`, 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrs_Split(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
eval string
|
||||
want any
|
||||
wantErr bool
|
||||
}{
|
||||
{desc: "split 1", eval: `strs:split "1,2,3" ","`, want: builtins.StringSlice{"1", "2", "3"}},
|
||||
{desc: "split 2", eval: `strs:split "1,2,3" ";"`, want: builtins.StringSlice{"1,2,3"}},
|
||||
{desc: "split 3", eval: `strs:split "" ";"`, want: builtins.StringSlice{""}},
|
||||
{desc: "split 4", eval: `strs:split " " ";"`, want: builtins.StringSlice{" "}},
|
||||
|
||||
{desc: "split by char 1", eval: `strs:split "123"`, want: builtins.StringSlice{"1", "2", "3"}},
|
||||
|
||||
{desc: "split max 1", eval: `strs:split "1,2,3" "," -max 2`, want: builtins.StringSlice{"1", "2,3"}},
|
||||
{desc: "split max 2", eval: `strs:split "1,2,3" "," -max 5`, want: builtins.StringSlice{"1", "2", "3"}},
|
||||
|
||||
{desc: "split by char max 1", eval: `strs:split "12345" -max 3`, want: builtins.StringSlice{"1", "2", "345"}},
|
||||
|
||||
{desc: "err 1", eval: `strs:split "1,2,3" -max []`, 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrs_Join(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
eval string
|
||||
want any
|
||||
wantErr bool
|
||||
}{
|
||||
{desc: "join 1", eval: `strs:join [1 2 3] ","`, want: "1,2,3"},
|
||||
{desc: "join 2", eval: `strs:join [a b c] " "`, want: "a b c"},
|
||||
{desc: "join 3", eval: `strs:join [a b c] ""`, want: "abc"},
|
||||
{desc: "join 4", eval: `strs:join [a b c]`, want: "abc"},
|
||||
{desc: "join 5", eval: `strs:join (itrs:from [a b c]) ","`, want: "a,b,c"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
inst := ucl.New(
|
||||
ucl.WithModule(builtins.Itrs()),
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ func Time() ucl.Module {
|
|||
Name: "time",
|
||||
Builtins: map[string]ucl.BuiltinHandler{
|
||||
"from-unix": timeFromUnix,
|
||||
"sleep": timeSleep,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -24,3 +25,18 @@ func timeFromUnix(ctx context.Context, args ucl.CallArgs) (any, error) {
|
|||
|
||||
return time.Unix(int64(ux), 0).UTC(), nil
|
||||
}
|
||||
|
||||
func timeSleep(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var secs int
|
||||
|
||||
if err := args.Bind(&secs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(time.Duration(secs) * time.Second):
|
||||
return nil, nil
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,3 +35,21 @@ func TestTime_FromUnix(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTime_Sleep(t *testing.T) {
|
||||
t.Run("should terminate on cancelled context", func(t *testing.T) {
|
||||
st := time.Now()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
inst := ucl.New(
|
||||
ucl.WithModule(builtins.Time()),
|
||||
)
|
||||
|
||||
_, err := inst.Eval(ctx, `time:sleep 1`)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "context canceled", err.Error())
|
||||
assert.True(t, time.Now().Sub(st) < time.Second)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -80,20 +80,25 @@ func TestInst_Eval(t *testing.T) {
|
|||
{desc: "map 6", expr: `set x [a:"A" b:"B" c:"C"] ; firstarg ["one":$x.c "two":$x.b "three":$x.a]`, want: map[string]any{"one": "C", "two": "B", "three": "A"}},
|
||||
|
||||
// Dots
|
||||
{desc: "dot 1", expr: `set x [1 2 3] ; $x.(0)`, want: 1},
|
||||
{desc: "dot 2", expr: `set x [1 2 3] ; $x.(1)`, want: 2},
|
||||
{desc: "dot 3", expr: `set x [1 2 3] ; $x.(2)`, want: 3},
|
||||
{desc: "dot 4", expr: `set x [1 2 3] ; $x.(3)`, want: nil},
|
||||
{desc: "dot 5", expr: `set x [1 2 3] ; $x.(add 1 1)`, want: 3},
|
||||
{desc: "dot 6", expr: `set x [alpha:"hello" bravo:"world"] ; $x.alpha`, want: "hello"},
|
||||
{desc: "dot 7", expr: `set x [alpha:"hello" bravo:"world"] ; $x.bravo`, want: "world"},
|
||||
{desc: "dot 8", expr: `set x [alpha:"hello" bravo:"world"] ; $x.charlie`, want: nil},
|
||||
{desc: "dot 9", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("alpha")`, want: "hello"},
|
||||
{desc: "dot 10", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("bravo")`, want: "world"},
|
||||
{desc: "dot 11", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("charlie")`, want: nil},
|
||||
{desc: "dot 12", expr: `set x [MORE:"stuff"] ; $x.("more" | toUpper)`, want: "stuff"},
|
||||
{desc: "dot 13", expr: `set x [MORE:"stuff"] ; $x.(toUpper ("more"))`, want: "stuff"},
|
||||
{desc: "dot 14", expr: `set x [MORE:"stuff"] ; x.y`, want: nil},
|
||||
{desc: "dot expr 1", expr: `set x [1 2 3] ; $x.(0)`, want: 1},
|
||||
{desc: "dot expr 2", expr: `set x [1 2 3] ; $x.(1)`, want: 2},
|
||||
{desc: "dot expr 3", expr: `set x [1 2 3] ; $x.(2)`, want: 3},
|
||||
{desc: "dot expr 4", expr: `set x [1 2 3] ; $x.(3)`, want: nil},
|
||||
{desc: "dot expr 5", expr: `set x [1 2 3] ; $x.(add 1 1)`, want: 3},
|
||||
{desc: "dot expr 6", expr: `set x [1 2 3] ; $x.(-1)`, want: 3},
|
||||
{desc: "dot expr 7", expr: `set x [1 2 3] ; $x.(-2)`, want: 2},
|
||||
{desc: "dot expr 8", expr: `set x [1 2 3] ; $x.(-3)`, want: 1},
|
||||
{desc: "dot expr 9", expr: `set x [1 2 3] ; $x.(-4)`, want: nil},
|
||||
|
||||
{desc: "dot idents 1", expr: `set x [alpha:"hello" bravo:"world"] ; $x.alpha`, want: "hello"},
|
||||
{desc: "dot idents 2", expr: `set x [alpha:"hello" bravo:"world"] ; $x.bravo`, want: "world"},
|
||||
{desc: "dot idents 3", expr: `set x [alpha:"hello" bravo:"world"] ; $x.charlie`, want: nil},
|
||||
{desc: "dot idents 4", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("alpha")`, want: "hello"},
|
||||
{desc: "dot idents 5", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("bravo")`, want: "world"},
|
||||
{desc: "dot idents 6", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("charlie")`, want: nil},
|
||||
{desc: "dot idents 7", expr: `set x [MORE:"stuff"] ; $x.("more" | toUpper)`, want: "stuff"},
|
||||
{desc: "dot idents 8", expr: `set x [MORE:"stuff"] ; $x.(toUpper ("more"))`, want: "stuff"},
|
||||
{desc: "dot idents 9", expr: `set x [MORE:"stuff"] ; x.y`, want: nil},
|
||||
|
||||
{desc: "parse comments 1", expr: parseComments1, wantErr: ucl.ErrNotConvertable},
|
||||
{desc: "parse comments 2", expr: parseComments2, wantErr: ucl.ErrNotConvertable},
|
||||
|
|
|
@ -437,7 +437,7 @@ func (ia invocationArgs) fork(args []Object) invocationArgs {
|
|||
inst: ia.inst,
|
||||
ec: ia.ec,
|
||||
args: args,
|
||||
kwargs: make(map[string]*ListObject),
|
||||
kwargs: nil,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -493,7 +493,7 @@ func TestBuiltins_Procs(t *testing.T) {
|
|||
set goodbye (makeGreeter "Goodbye cruel")
|
||||
$goodbye "world"
|
||||
|
||||
call (makeGreeter "Quick") "call me"
|
||||
call (makeGreeter "Quick") ["call me"]
|
||||
|
||||
`, want: "Hello, world\nGoodbye cruel, world\nQuick, call me\n(nil)\n"},
|
||||
{desc: "modifying closed over variables", expr: `
|
||||
|
@ -505,8 +505,8 @@ func TestBuiltins_Procs(t *testing.T) {
|
|||
}
|
||||
|
||||
set er (makeSetter)
|
||||
echo (call $er "xxx")
|
||||
echo (call $er "yyy")
|
||||
echo (call $er ["xxx"])
|
||||
echo (call $er ["yyy"])
|
||||
`, want: "Xxxx\nXxxxyyy\n(nil)\n"},
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue